import { Controller } from "@hotwired/stimulus"; export default class extends Controller { static targets = ["item", "search", "current", "placeholder"]; connect() { // TODO: Stimulus >1 this.newArrayValueURL = new URL(window.location.origin); const [ pathname, search ] = this.element.dataset.arrayNewArrayValue.split("?"); this.newArrayValueURL.pathname = pathname; this.newArrayValueURL.search = `?${search}`; 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; } cancelWithEscape(event) { if (event?.key !== "Escape") return; this.cancel(); } /* * Al cancelar, se vuelve al estado original de la lista */ cancel(event = undefined) { 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; for (const itemTarget of this.itemTargets) { if (!itemTarget.dataset.value) continue; if (!this.isChecked(itemTarget)) continue; this.originalValue.push(itemTarget.dataset.value); this.newArrayValueURL.searchParams.set("value", itemTarget.dataset?.sendValue || itemTarget.dataset?.value); const placeholder = this.placeholderTarget.content.firstElementChild.cloneNode(true); this.currentTarget.appendChild(placeholder); fetch(this.newArrayValueURL, { signal }) .then((response) => response.text()) .then((body) => { const template = document.createElement("template"); template.innerHTML = body; placeholder.replaceWith(template.content.firstElementChild); }); } // TODO: Stimulus >1 this.element.dataset.arrayOriginalValue = JSON.stringify( this.originalValue, ); } }