widgets: add MultiSelectWidget (#75655)
gitea/gadjo/pipeline/head This commit looks good Details

from combo.apps.dataviz
This commit is contained in:
Lauréline Guérin 2023-03-21 14:12:09 +01:00
parent 2b824e5b5a
commit 39dbdabcd3
No known key found for this signature in database
GPG Key ID: 1FAB9B9B4F93D473
6 changed files with 157 additions and 0 deletions

1
.gitignore vendored
View File

@ -5,6 +5,7 @@
/gadjo.egg-info
/gadjo/locale/fr/LC_MESSAGES/django.mo
/gadjo/static/css/gadjo.css
/gadjo/static/css/gadjo.multiselectwidget.css
/gadjo/static/css/icons
node_modules
MANIFEST

0
gadjo/forms/__init__.py Normal file
View File

46
gadjo/forms/widgets.py Normal file
View File

@ -0,0 +1,46 @@
import django
from django import forms
class MultiSelectWidget(forms.MultiWidget):
template_name = 'gadjo/widgets/multiselectwidget.html'
class Media:
js = ('js/gadjo.multiselectwidget.js',)
css = {'all': ('css/gadjo.multiselectwidget.css',)}
def __init__(self, attrs=None):
self.attrs = attrs
widgets = [forms.Select(attrs=attrs)]
super().__init__(widgets, attrs)
def get_context(self, name, value, attrs):
if not isinstance(value, list):
value = [value]
self.widgets = []
for _ in range(max(len(value), 1)):
self.widgets.append(forms.Select(attrs=self.attrs, choices=self.choices))
# all subwidgets must have the same name
if django.VERSION >= (3, 1):
self.widgets_names = [''] * len(self.widgets)
return super().get_context(name, value, attrs)
else:
context = super().get_context(name, value, attrs)
subwidgets = context['widget']['subwidgets']
for widget in subwidgets:
widget['name'] = widget['name'].rsplit('_', 1)[0]
return context
def decompress(self, value):
return value or []
def value_from_datadict(self, data, files, name):
values = [x for x in data.getlist(name) if x]
# remove duplicates while keeping order
return list(dict.fromkeys(values))
def id_for_label(self, id_):
return id_

View File

@ -0,0 +1,35 @@
.gadjo-multi-select-widget {
&--field {
margin-bottom: 0.2em;
}
&--select-button-container {
display: flex;
gap: 0.5em;
}
&--field {
select {
min-width: 0;
}
button {
margin-top: auto;
margin-bottom: auto;
}
&:first-of-type .gadjo-multi-select-widget--button-remove {
display: none;
}
}
&--button-add::before {
content: "\f067"; /* plus */
font-family: FontAwesome;
}
&--button-remove::before {
content: "\f068"; /* minus */
font-family: FontAwesome;
}
}

View File

@ -0,0 +1,58 @@
const multiSelectWidget = (function () {
const addRow = function () {
const widget = this.closest('.gadjo-multi-select-widget')
event.preventDefault()
/* get last row node */
const rows = widget.querySelectorAll('.gadjo-multi-select-widget--field')
const lastRow = rows[rows.length - 1]
/* clone the row */
const newRow = lastRow.cloneNode(true)
/* set new label and ids */
const rowLabel = widget.dataset.rowLabel
const newLabel = rowLabel + ' ' + rows.length
newRow.querySelector('label').textContent = newLabel
const rowId = widget.dataset.rowId
const newId = rowId + '_' + rows.length
newRow.querySelector('label').setAttribute('for', newId)
newRow.querySelector('select').setAttribute('id', newId)
/* add new row after the last row */
lastRow.parentNode.insertBefore(newRow, lastRow.nextSibling)
const removeButton = newRow.querySelector('.gadjo-multi-select-widget--button-remove')
removeButton.addEventListener('click', removeRow)
}
const removeRow = function (event) {
event.preventDefault()
const field = this.closest('.content')
let row = this.closest('.gadjo-multi-select-widget--field')
row.remove()
field.dispatchEvent(new Event('change'))
}
const init = function (container) {
const widgets = container.querySelectorAll('.gadjo-multi-select-widget')
if (!widgets.length) return
widgets.forEach(function (widget) {
const deletBtn = widget.querySelector('.gadjo-multi-select-widget--button-remove')
const addBtn = widget.querySelector('.gadjo-multi-select-widget--button-add')
addBtn.removeEventListener('click', addRow)
addBtn.addEventListener('click', addRow)
deletBtn.removeEventListener('click', removeRow)
deletBtn.addEventListener('click', removeRow)
})
}
return {
init,
}
})()
window.addEventListener('DOMContentLoaded', () => multiSelectWidget.init(document))

View File

@ -0,0 +1,17 @@
{% load i18n %}
<div class="gadjo-multi-select-widget" data-row-id="{{ widget.name }}" data-row-label="{% trans "Value" %}">
<div class="gadjo-multi-select-widget--fields" role="group" aria-labelledby="{{ widget.name }}_title">
{% for widget in widget.subwidgets %}
<div class="gadjo-multi-select-widget--field">
<label for="{{ widget.name }}_{{ forloop.counter }}" class="sr-only">{% trans "Value" %} {{ forloop.counter }}</label>
<div class="gadjo-multi-select-widget--select-button-container">
{% include widget.template_name %}
<button type="button" name="{{ widget.name }}$remove_element" class="gadjo-multi-select-widget--button-remove" title="{% trans "Remove" %}" aria-label="{% trans "Remove value" %} {{ forloop.counter }}"></button>
</div>
</div>
{% endfor %}
</div>
<button type="button" name="{{ widget.name }}$add_element" class="gadjo-multi-select-widget--button-add" title="{% trans "Add" %}" aria-label="{% trans "Add" %}"></button>
</div>