mirror of
https://github.com/ae-utbm/sith.git
synced 2025-03-29 00:27:13 +00:00
feat: add x-limited-choices directive
This commit is contained in:
parent
6c8743a403
commit
57522d89c2
@ -1,7 +1,8 @@
|
|||||||
|
import { limitedChoices } from "#core:alpine/limited-choices";
|
||||||
import sort from "@alpinejs/sort";
|
import sort from "@alpinejs/sort";
|
||||||
import Alpine from "alpinejs";
|
import Alpine from "alpinejs";
|
||||||
|
|
||||||
Alpine.plugin(sort);
|
Alpine.plugin([sort, limitedChoices]);
|
||||||
window.Alpine = Alpine;
|
window.Alpine = Alpine;
|
||||||
|
|
||||||
window.addEventListener("DOMContentLoaded", () => {
|
window.addEventListener("DOMContentLoaded", () => {
|
||||||
|
69
core/static/bundled/alpine/limited-choices.ts
Normal file
69
core/static/bundled/alpine/limited-choices.ts
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
import type { Alpine as AlpineType } from "alpinejs";
|
||||||
|
|
||||||
|
export function limitedChoices(Alpine: AlpineType) {
|
||||||
|
/**
|
||||||
|
* Directive to limit the number of elements
|
||||||
|
* that can be selected in a group of checkboxes.
|
||||||
|
*
|
||||||
|
* When the max numbers of selectable elements is reached,
|
||||||
|
* new elements will still be inserted, but oldest ones will be deselected.
|
||||||
|
* For example, if checkboxes A, B and C have been selected and the max
|
||||||
|
* number of selections is 3, then selecting D will result in having
|
||||||
|
* B, C and D selected.
|
||||||
|
*
|
||||||
|
* # Example in template
|
||||||
|
* ```html
|
||||||
|
* <div x-data="{nbMax: 2}", x-limited-choices="nbMax">
|
||||||
|
* <button @click="nbMax += 1">Click me to increase the limit</button>
|
||||||
|
* <input type="checkbox" value="A" name="foo">
|
||||||
|
* <input type="checkbox" value="B" name="foo">
|
||||||
|
* <input type="checkbox" value="C" name="foo">
|
||||||
|
* <input type="checkbox" value="D" name="foo">
|
||||||
|
* </div>
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
Alpine.directive(
|
||||||
|
"limited-choices",
|
||||||
|
(el, { expression }, { evaluateLater, effect }) => {
|
||||||
|
const getMaxChoices = evaluateLater(expression);
|
||||||
|
let maxChoices: number;
|
||||||
|
const inputs: HTMLInputElement[] = Array.from(
|
||||||
|
el.querySelectorAll("input[type='checkbox']"),
|
||||||
|
);
|
||||||
|
const checked = [] as HTMLInputElement[];
|
||||||
|
|
||||||
|
const manageDequeue = () => {
|
||||||
|
if (checked.length <= maxChoices) {
|
||||||
|
// There isn't too many checkboxes selected. Nothing to do
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const popped = checked.splice(0, checked.length - maxChoices);
|
||||||
|
for (const p of popped) {
|
||||||
|
p.checked = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const input of inputs) {
|
||||||
|
input.addEventListener("change", (_e) => {
|
||||||
|
if (input.checked) {
|
||||||
|
checked.push(input);
|
||||||
|
} else {
|
||||||
|
checked.splice(checked.indexOf(input), 1);
|
||||||
|
}
|
||||||
|
manageDequeue();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
effect(() => {
|
||||||
|
getMaxChoices((value: string) => {
|
||||||
|
const previousValue = maxChoices;
|
||||||
|
maxChoices = Number.parseInt(value);
|
||||||
|
if (maxChoices < previousValue) {
|
||||||
|
// The maximum number of selectable items has been lowered.
|
||||||
|
// Some currently selected elements may need to be removed
|
||||||
|
manageDequeue();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
@ -63,7 +63,13 @@
|
|||||||
{%- for role in role_list %}
|
{%- for role in role_list %}
|
||||||
{%- set count = [0] %}
|
{%- set count = [0] %}
|
||||||
{%- set role_data = election_form.data.getlist(role.title) if role.title in election_form.data else [] %}
|
{%- set role_data = election_form.data.getlist(role.title) if role.title in election_form.data else [] %}
|
||||||
<tbody data-max-choice="{{role.max_choice}}" class="role{{ ' role_error' if role.title in election_form.errors else '' }}{{ ' role__multiple-choices' if role.max_choice > 1 else ''}}">
|
|
||||||
|
<tbody
|
||||||
|
{% if role.max_choice > 1 -%}
|
||||||
|
x-data x-limited-choices="{{ role.max_choice }}"
|
||||||
|
{%- endif %}
|
||||||
|
class="role {% if role.title in election_form.errors %}role_error{% endif %}"
|
||||||
|
>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="role_title">
|
<td class="role_title">
|
||||||
<div class="role_text">
|
<div class="role_text">
|
||||||
@ -197,30 +203,3 @@
|
|||||||
</section>
|
</section>
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block script %}
|
|
||||||
{{ super() }}
|
|
||||||
<script type="text/javascript">
|
|
||||||
document.querySelectorAll('.role__multiple-choices').forEach(setupRestrictions);
|
|
||||||
|
|
||||||
function setupRestrictions(role) {
|
|
||||||
var selectedChoices = [];
|
|
||||||
role.querySelectorAll('input').forEach(setupRestriction);
|
|
||||||
|
|
||||||
function setupRestriction(choice) {
|
|
||||||
if (choice.checked)
|
|
||||||
selectedChoices.push(choice);
|
|
||||||
choice.addEventListener('change', onChange);
|
|
||||||
|
|
||||||
function onChange() {
|
|
||||||
if (choice.checked)
|
|
||||||
selectedChoices.push(choice);
|
|
||||||
else
|
|
||||||
selectedChoices.splice(selectedChoices.indexOf(choice), 1);
|
|
||||||
while (selectedChoices.length > role.dataset.maxChoice)
|
|
||||||
selectedChoices.shift().checked = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
{% endblock %}
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user