WIP: JS Tree improvements

* adds optional short_name rendering for selectable codes
* refactors autocomplete field for compensation state into custom js tree widget
* adds single select (radio) alternative to tree widget templates
pull/161/head
mpeltriaux 2 years ago
parent 18789c231d
commit a5d24e6db5

@ -65,24 +65,23 @@ class KonovaCode(models.Model):
ret_val += ", " + self.parent.long_name
return ret_val
def add_children(self):
def add_children(self, order_by: str = "long_name"):
""" Adds all children (resurcively until leaf) as .children to the KonovaCode
Returns:
code (KonovaCode): The manipulated KonovaCode instance
"""
if self.is_leaf:
return None
return self
children = KonovaCode.objects.filter(
code_lists__in=self.code_lists.all(),
parent=self
).order_by(
"long_name"
order_by
)
self.children = children
for child in children:
child.add_children()
child.add_children(order_by)
return self

@ -17,7 +17,8 @@ from codelist.models import KonovaCode
from codelist.settings import CODELIST_BIOTOPES_ID, CODELIST_COMPENSATION_ACTION_ID, CODELIST_BIOTOPES_EXTRA_CODES_ID, \
CODELIST_COMPENSATION_ACTION_DETAIL_ID
from compensation.models import CompensationDocument, EcoAccountDocument
from intervention.inputs import CompensationActionTreeCheckboxSelectMultiple
from intervention.inputs import CompensationActionTreeCheckboxSelectMultiple, \
CompensationStateTreeRadioSelect
from konova.contexts import BaseContext
from konova.forms import BaseModalForm, NewDocumentModalForm, RemoveModalForm
from konova.models import DeadlineType
@ -156,22 +157,12 @@ class NewStateModalForm(BaseModalForm):
What has been on this area before changes/compensations have been applied and what will be the result ('after')?
"""
biotope_type = forms.ModelChoiceField(
biotope_type = forms.MultipleChoiceField(
label=_("Biotope Type"),
label_suffix="",
required=True,
help_text=_("Select the biotope type"),
queryset=KonovaCode.objects.filter(
is_archived=False,
is_leaf=True,
code_lists__in=[CODELIST_BIOTOPES_ID],
),
widget=autocomplete.ModelSelect2(
url="codes-biotope-autocomplete",
attrs={
"data-placeholder": _("Biotope Type"),
}
),
widget=CompensationStateTreeRadioSelect(),
)
biotope_extra = forms.ModelMultipleChoiceField(
label=_("Biotope additional type"),
@ -272,7 +263,7 @@ class EditCompensationStateModalForm(NewStateModalForm):
super().__init__(*args, **kwargs)
self.form_title = _("Edit state")
form_data = {
"biotope_type": self.state.biotope_type,
"biotope_type": self.state.biotope_type.id,
"biotope_extra": self.state.biotope_type_details.all(),
"surface": self.state.surface,
}

@ -1,6 +1,6 @@
from django import forms
from codelist.models import KonovaCode
from codelist.settings import CODELIST_COMPENSATION_ACTION_ID
from codelist.settings import CODELIST_COMPENSATION_ACTION_ID, CODELIST_BIOTOPES_ID
class DummyFilterInput(forms.HiddenInput):
@ -38,7 +38,17 @@ class TreeCheckboxSelectMultiple(forms.CheckboxSelectMultiple):
""" Provides multiple selection of parent-child data
"""
template_name = "konova/widgets/checkbox-tree-select.html"
template_name = "konova/widgets/tree/checkbox/checkbox-tree-select.html"
class meta:
abstract = True
class TreeRadioSelect(forms.RadioSelect):
""" Provides single selection of parent-child data
"""
template_name = "konova/widgets/tree/radio/radio-tree-select.html"
class meta:
abstract = True
@ -68,6 +78,30 @@ class KonovaCodeTreeCheckboxSelectMultiple(TreeCheckboxSelectMultiple):
return context
class KonovaCodeTreeRadioSelect(TreeRadioSelect):
""" Provides single selection of KonovaCode
"""
filter = None
def __init__(self, *args, **kwargs):
self.code_list = kwargs.pop("code_list", None)
self.filter = kwargs.pop("filter", {})
super().__init__(*args, **kwargs)
def get_context(self, name, value, attrs):
context = super().get_context(name, value, attrs)
codes = KonovaCode.objects.filter(
**self.filter,
)
codes = [
parent_code.add_children()
for parent_code in codes
]
context["codes"] = codes
return context
class CompensationActionTreeCheckboxSelectMultiple(KonovaCodeTreeCheckboxSelectMultiple):
""" Provides multiple selection of CompensationActions
@ -79,4 +113,31 @@ class CompensationActionTreeCheckboxSelectMultiple(KonovaCodeTreeCheckboxSelectM
self.filter = {
"code_lists__in": [CODELIST_COMPENSATION_ACTION_ID],
"parent": None,
}
}
class CompensationStateTreeRadioSelect(KonovaCodeTreeRadioSelect):
""" Provides single selection of CompensationState
"""
filter = None
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.filter = {
"code_lists__in": [CODELIST_BIOTOPES_ID],
"parent": None,
"is_archived": False,
}
def get_context(self, name, value, attrs):
context = super().get_context(name, value, attrs)
codes = KonovaCode.objects.filter(
**self.filter,
)
codes = [
parent_code.add_children("short_name")
for parent_code in codes
]
context["codes"] = codes
return context

@ -10,12 +10,15 @@
{% fa5_icon 'angle-down' %}
</span>
{% endif %}
{% if code.short_name %}
({{code.short_name}})
{% endif %}
{{code.long_name}}
</label>
{% if not code.is_leaf %}
<div id="children_{{code.pk|unlocalize}}" data-toggle="collapse" class="collapse tree-element-children">
{% with code.children as codes %}
{% include 'konova/widgets/checkbox-tree-select-content.html' %}
{% include 'konova/widgets/tree/checkbox/checkbox-tree-select-content.html' %}
{% endwith %}
</div>
{% endif %}

@ -5,7 +5,7 @@
</div>
<div id="tree-root">
{% include 'konova/widgets/checkbox-tree-select-content.html' %}
{% include 'konova/widgets/tree/checkbox/checkbox-tree-select-content.html' %}
</div>
<script>

@ -0,0 +1,25 @@
{% load l10n fontawesome_5 %}
{% for code in codes %}
<div class="ml-4 tree-element">
<label class="tree-label collapsed" role="{% if not code.is_leaf%}button{% endif %}" for="input_{{code.pk|unlocalize}}" id="{{code.pk|unlocalize}}" data-toggle="collapse" data-target="#children_{{code.pk|unlocalize}}" aria-expanded="true" aria-controls="children_{{code.pk|unlocalize}}">
{% if code.is_leaf%}
<input class="tree-input" id="input_{{code.pk|unlocalize}}" name="{{ widget.name }}" type="radio" value="{{code.pk|unlocalize}}" {% if code.pk|unlocalize in widget.value %}checked{% endif %}/>
{% else %}
<span class="collapse-icn">
{% fa5_icon 'angle-down' %}
</span>
{% endif %}
{% if code.short_name %}
({{code.short_name}})
{% endif %}
{{code.long_name}}
</label>
{% if not code.is_leaf %}
<div id="children_{{code.pk|unlocalize}}" data-toggle="collapse" class="collapse tree-element-children">
{% with code.children as codes %}
{% include 'konova/widgets/tree/radio/radio-tree-select-content.html' %}
{% endwith %}
</div>
{% endif %}
</div>
{% endfor %}

@ -0,0 +1,70 @@
{% load i18n %}
<div class="ml-4 mb-4">
<input id="tree-search-input" class="form-control" type="text" placeholder="{% trans 'Search' %}"/>
</div>
<div id="tree-root">
{% include 'konova/widgets/tree/radio/radio-tree-select-content.html' %}
</div>
<script>
function toggleSelectedCssClass(element){
element = $(element);
var cssClass = "badge rlp-r"
var directParent = element.closest(".tree-element-children")
var root = element.parents(".tree-element-children")
var otherCheckedInputsOfParent = directParent.find('.tree-input:checked');
var otherCheckedInputsOfRoot = root.find('.tree-input:checked');
if(otherCheckedInputsOfParent.length == 0){
var parentLabel = directParent.siblings(".tree-label");
parentLabel.removeClass(cssClass)
if(otherCheckedInputsOfRoot.length == 0){
var rootLabel = root.siblings(".tree-label")
rootLabel.removeClass(cssClass)
}
}else{
var rootAndParentLabel = root.siblings(".tree-label");
rootAndParentLabel.addClass(cssClass);
}
}
function changeHandler(event){
toggleSelectedCssClass(this);
}
function searchInputHandler(event){
var elem = $(this);
var val = elem.val()
var allTreeElements = $(".tree-element")
var allTreeElementsContain = $(".tree-element").filter(function(){
var reg = new RegExp(val, "i");
return reg.test($(this).text());
}
);
if(val.length > 0){
// Hide everything
allTreeElements.hide()
// Now show again everything matching the query
allTreeElementsContain.show()
}else{
// Show everything if no query exists
allTreeElements.show()
}
}
// Add event listener on search input
$("#tree-search-input").keyup(searchInputHandler)
// Add event listener on changed checkboxes
$(".tree-input").change(changeHandler);
// initialize all pre-checked checkboxes (e.g. on an edit form)
var preCheckedElements = $(".tree-input:checked");
preCheckedElements.each(function (index, element){
toggleSelectedCssClass(element);
})
</script>
Loading…
Cancel
Save