Skip to content

Commit

Permalink
fix: Improved blur handling
Browse files Browse the repository at this point in the history
PR #4
  • Loading branch information
lucbelliveau authored Dec 12, 2022
1 parent 8ac1be6 commit 5681830
Show file tree
Hide file tree
Showing 11 changed files with 57 additions and 20 deletions.
16 changes: 12 additions & 4 deletions autocomplete/autocomplete.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ class Meta:
placeholder (str or None): The placeholder text used on the component
Defaults to None.
required (bool): If set the control is marked as required.
no_result_text (str): The string displayed when no results are found.
Defaults to "No results found."
Expand Down Expand Up @@ -108,6 +110,9 @@ class Meta:
# The placeholder text. (Typically something like "Type to search...")
placeholder = None

# If set to True the HTML control will be marked as required
required = False

# The minimum search length to perform a search and show the dropdown.
minimum_search_length = 3

Expand Down Expand Up @@ -428,11 +433,12 @@ def put(self, request, method):
template.render(
{
"name": self.name,
"required": self.required,
"no_result_text": self.no_result_text,
"narrow_search_text": self.narrow_search_text,
"route_name": self.get_route_name(),
"multiselect": self.multiselect,
"values": self.item_values(items, True),
"values": list(self.item_values(items, True)),
"item": target_item,
"toggle": items,
"swap_oob": data.get("remove", False),
Expand Down Expand Up @@ -497,12 +503,13 @@ def get(self, request, method):
template.render(
{
"name": self.name,
"required": self.required,
"route_name": self.get_route_name(),
"label": self.label,
"placeholder": self.placeholder,
"multiselect": self.multiselect,
"values": self.item_values(selected_options),
"selected_items": selected_options,
"values": list(self.item_values(selected_options)),
"selected_items": list(selected_options),
"no_result_text": self.no_result_text,
"narrow_search_text": self.narrow_search_text,
},
Expand All @@ -527,11 +534,12 @@ def get(self, request, method):
template.render(
{
"name": self.name,
"required": self.required,
"no_result_text": self.no_result_text,
"narrow_search_text": self.narrow_search_text,
"route_name": self.get_route_name(),
"show": show,
"items": items,
"items": list(items),
"total_results": total_results,
},
request,
Expand Down
5 changes: 5 additions & 0 deletions autocomplete/static/autocomplete/css/autocomplete.css
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,11 @@
border-radius: 0;
}

.phac_aspc_form_autocomplete .ac_required_input {
opacity: 0;
position: absolute;
}

.phac_aspc_form_autocomplete ul.ac_container li.search-indicator svg{
max-width:25px;
max-height:25px;
Expand Down
2 changes: 0 additions & 2 deletions autocomplete/static/autocomplete/js/autocomplete.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@ function phac_aspc_autocomplete_blur_handler(name, sync=false) {
if (!sync) {
el.value = '';
} else {
console.log('setting value!');
el.value = data_el.getAttribute('data-phac-aspc-autocomplete');
console.log(el.value);
}
document.getElementById(name + '__items').classList.remove('show');
}
Expand Down
7 changes: 4 additions & 3 deletions autocomplete/templates/autocomplete/chip.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@
<span>{{ item.label }}</span>
<a
hx-put="{% url route_name method='toggle' %}"
hx-vals='{"remove": true, "item": "{{ item.value|urlencode }}"}'
hx-include="#{{ route_name }} > input[name='{{ name }}']"
hx-params="{{ route_name }},item,remove"
hx-vals='{"remove": true, "item": "{{ item.value|escapejs }}"}'
hx-include="#{{ route_name }}"
hx-swap="delete"
tabindex="-1"
>
Expand All @@ -17,4 +18,4 @@
</path>
</svg>
</a>
</li>
</li>
12 changes: 9 additions & 3 deletions autocomplete/templates/autocomplete/component.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
<div
id="{{ route_name }}__container"
class="phac_aspc_form_autocomplete"
hx-include="unset"
>
{# Hidden input elements used to maintain the component's state #}
{# and used when submitting forms #}
Expand Down Expand Up @@ -40,9 +41,6 @@
</li>
</ul>
<div class="dropdown-menu" id="{{ route_name }}__items"></div>
{% comment %} <div class="htmx-indicator">
<img src="https://htmx.org/img/bars.svg" />
</div> {% endcomment %}
</div>
{# This code snippet loads the required CSS and JS if not already loaded. #}
<script>
Expand All @@ -58,5 +56,13 @@
if (!Array.from(document.querySelectorAll('link')).map(s => s.href).includes(ln.href)) {
document.getElementsByTagName('head')[0].appendChild(ln);
}
document.querySelector('#{{ route_name }}__container .ac_container').addEventListener(
'click',
function(evt) {
if (evt.target === this) {
document.getElementById('{{ route_name }}__textinput').focus();
}
}
);
})();
</script>
10 changes: 6 additions & 4 deletions autocomplete/templates/autocomplete/item.html
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
{% load autocomplete %}
<a
id="{{ route_name }}__item__{{ item.value|urlencode }}"
id="{{ route_name }}__item__{{ item.value|make_id }}"
class="item {{ item.selected|yesno:'selected,' }}"
onblur="phac_aspc_autocomplete_blur_handler('{{ route_name }}', {{ multiselect|yesno:'false,true' }})"
{% if not item.selected %}
href="#"
hx-put="{% url route_name method='toggle' %}"
hx-include="#{{ route_name }} > input[name='{{ name }}']"
hx-vals='{"item": "{{ item.value|urlencode }}"}'
hx-params="{{ route_name }},item"
hx-include="#{{ route_name }}"
hx-vals='{"item": "{{ item.value|escapejs }}"}'
{% endif %}
hx-swap="outerHTML"
{% if swap_oob %}
hx-swap-oob="outerHTML:#{{ route_name }}__item__{{ item.value|urlencode }}"
hx-swap-oob="outerHTML:#{{ route_name }}__item__{{ item.value|make_id }}"
{% endif %}
>
{{ item.label }}
Expand Down
5 changes: 4 additions & 1 deletion autocomplete/templates/autocomplete/textinput.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,12 @@
{% if not multiselect and selected_items|length == 1 %}
value="{{ selected_items.0.label }}"
{% endif %}
{% if selected_items|length == 0 %}
required
{% endif %}
hx-get="{% url route_name method='items' %}"
hx-trigger="keyup changed delay:250ms,click,focus,search"
hx-include="#{{ route_name }} > input[name='{{ name }}']"
hx-include="#{{ route_name }}"
hx-target="#{{ route_name }}__items"
hx-swap="outerHTML"
{% if swap_oob %}
Expand Down
3 changes: 3 additions & 0 deletions autocomplete/templates/autocomplete/values.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,6 @@
{% for value in values %}
<input type="hidden" name="{{ name }}" value="{{ value }}" />
{% endfor %}
{% if required and values|length == 0 %}
<input type="text" name="{{ name }}" required class="ac_required_input">
{% endif %}
8 changes: 8 additions & 0 deletions autocomplete/templatetags/autocomplete.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,22 @@
"""
Django template tags to facilitate rendering of the component
"""
import hashlib

from django import template
from django import urls
from django.utils.html import format_html
from django.utils.http import urlencode
from django.template import loader
from django.template.defaultfilters import stringfilter

register = template.Library()

@register.filter
@stringfilter
def make_id(value):
"""Generate an ID given a string, to use as element IDs in HTML"""
return hashlib.sha1(value.encode('utf-8')).hexdigest()

@register.simple_tag
def autocomplete(name, selected=None):
Expand Down
7 changes: 5 additions & 2 deletions autocomplete/widgets.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ class Autocomplete(Widget):
name (str): The name of the component (must be unique)
options (dict): See [autocomplete.py](../autocomplete.py) for more info
label (str)
required (bool) Defaults to false
placeholder (str)
no_result_text (str) Defaults to "No results found."
narrow_search_text (str) Defaults to
Expand Down Expand Up @@ -41,6 +42,7 @@ def __init__(
super().__init__(attrs)
config = {
'name': name,
'required': opts.get('required', None),
'route_name': opts.get('route_name', None),
'label': opts.get('label', None),
'placeholder': opts.get('placeholder', None),
Expand Down Expand Up @@ -98,11 +100,12 @@ def get_context(self, name, value, attrs):
)

context['name'] = self.a_c.name
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'] = self.a_c.item_values(self.a_c, selected_options)
context['selected_items'] = selected_options
context['values'] = list(self.a_c.item_values(self.a_c, selected_options))
context['selected_items'] = list(selected_options)

return context
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[metadata]
name = django-htmx-autocomplete
version = 0.1.1
version = 0.1.2
description = A Django autocomplete component powered by htmx
long_description = file: README.md
long_description_content_type = text/markdown
Expand Down

0 comments on commit 5681830

Please sign in to comment.