5
0
Fork 0
mirror of https://0xacab.org/sutty/sutty synced 2024-11-22 19:46:23 +00:00
panel/app/javascript/controllers/array_controller.js

139 lines
3.7 KiB
JavaScript
Raw Normal View History

2024-07-20 18:21:03 +00:00
import { Controller } from "@hotwired/stimulus";
2024-05-17 18:24:08 +00:00
export default class extends Controller {
2024-05-27 19:40:09 +00:00
static targets = ["item", "search", "current", "placeholder"];
2024-05-17 18:24:08 +00:00
connect() {
// TODO: Stimulus >1
this.newArrayValueURL = new URL(window.location.origin);
2024-10-21 19:16:52 +00:00
const [ pathname, search ] = this.element.dataset.arrayNewArrayValue.split("?");
this.newArrayValueURL.pathname = pathname;
this.newArrayValueURL.search = `?${search}`;
2024-05-17 18:24:08 +00:00
this.originalValue = JSON.parse(this.element.dataset.arrayOriginalValue);
}
/*
* Al eliminar el ítem, buscamos por su ID y lo eliminamos del
* documento.
*/
remove(event) {
// TODO: Stimulus >1
event.preventDefault();
this.itemTargets
.find((x) => x.id === event.target.dataset.removeTargetParam)
?.remove();
}
/*
* Al buscar, eliminamos las tildes y mayúsculas para no depender de
* cómo se escribió.
*
* Luego buscamos eso en el valor limpio, ignorando los items que ya
* están activados.
*
* Si el término de búsqueda está vacío, volvemos a la lista original.
*/
search(event) {
const needle = this.searchTarget.value
.normalize("NFD")
.replace(/[\u0300-\u036f]/g, "")
.toLowerCase()
.trim();
if (needle) {
for (const itemTarget of this.itemTargets) {
itemTarget.style.display =
itemTarget.querySelector("input")?.checked ||
itemTarget.dataset.searchableValue.includes(needle)
? ""
: "none";
}
} else {
for (const itemTarget of this.itemTargets) {
itemTarget.style.display = "";
}
}
}
/*
* Obtiene el input de un elemento
*
* @param [HTMLElement]
* @return [HTMLElement,nil]
*/
inputFrom(target) {
if (target.tagName === "INPUT") return target;
return target.querySelector("input");
}
/*
* Detecta si el item es o contiene un checkbox/radio activado.
*
* @param [HTMLElement]
* @return [Bool]
*/
isChecked(itemTarget) {
return this.inputFrom(itemTarget)?.checked || false;
}
2024-06-05 21:00:55 +00:00
cancelWithEscape(event) {
if (event?.key !== "Escape") return;
this.cancel();
}
2024-05-17 18:24:08 +00:00
/*
* Al cancelar, se vuelve al estado original de la lista
*/
2024-06-05 21:00:55 +00:00
cancel(event = undefined) {
2024-05-17 18:24:08 +00:00
for (const itemTarget of this.itemTargets) {
const input = this.inputFrom(itemTarget);
input.checked = this.originalValue.includes(itemTarget.dataset.value);
}
}
/*
* Al aceptar, se envía todo el listado de valores nuevos al _backend_
* para que devuelva la representación de cada ítem en HTML. Además,
* se guarda el nuevo valor como la lista original, para la próxima
* cancelación.
*/
accept(event) {
this.currentTarget.innerHTML = "";
this.originalValue = [];
const signal = window.abortController?.signal;
2024-05-17 18:24:08 +00:00
for (const itemTarget of this.itemTargets) {
if (!itemTarget.dataset.value) continue;
if (!this.isChecked(itemTarget)) continue;
this.originalValue.push(itemTarget.dataset.value);
2024-07-05 14:56:48 +00:00
this.newArrayValueURL.searchParams.set("value", itemTarget.dataset?.sendValue || itemTarget.dataset?.value);
2024-05-17 18:24:08 +00:00
2024-05-27 19:40:09 +00:00
const placeholder = this.placeholderTarget.content.firstElementChild.cloneNode(true);
this.currentTarget.appendChild(placeholder);
fetch(this.newArrayValueURL, { signal })
2024-05-17 18:24:08 +00:00
.then((response) => response.text())
2024-05-27 19:40:09 +00:00
.then((body) => {
const template = document.createElement("template");
template.innerHTML = body;
placeholder.replaceWith(template.content.firstElementChild);
});
2024-05-17 18:24:08 +00:00
}
// TODO: Stimulus >1
this.element.dataset.arrayOriginalValue = JSON.stringify(
this.originalValue,
);
}
}