const SelectHook = { mounted() { const container = this.el; const trigger = container.querySelector('[id$="-trigger"]'); const dropdown = container.querySelector('[id$="-dropdown"]'); const search = container.querySelector('[id$="-search"]'); const options = container.querySelector('[id$="-options"]'); const hiddenInput = container.querySelector('input[type="hidden"]'); const selectedText = container.querySelector('[id$="-selected"]'); const toggleDropdown = (event) => { // Prevent the event from bubbling up to any parent form if (event) { event.preventDefault(); event.stopPropagation(); } const isExpanded = dropdown.classList.contains("block"); if (!isExpanded) { dropdown.classList.remove("hidden"); // Add animation classes dropdown.classList.add( "block", "animate-in", "fade-in", "duration-200", ); search.value = ""; filterOptions(""); search.focus(); } else { // Add animation classes for hiding dropdown.classList.remove("animate-in", "fade-in"); dropdown.classList.add("animate-out", "fade-out"); setTimeout(() => { dropdown.classList.remove("block", "animate-out", "fade-out"); dropdown.classList.add("hidden"); }, 200); } }; const filterOptions = (query) => { const items = options.querySelectorAll("li"); items.forEach((item) => { const text = item.textContent.toLowerCase(); if (text.includes(query.toLowerCase())) { item.style.display = ""; } else { item.style.display = "none"; } }); }; const selectOption = (option, event) => { // Prevent form submission if (event) { event.preventDefault(); event.stopPropagation(); } const value = option.dataset.value; const label = option.querySelector("span").textContent; // Update hidden input hiddenInput.value = value; // Update visible text selectedText.textContent = label; // Update check mark position options.querySelectorAll("li").forEach((li) => { const checkmark = li.querySelector("span:last-child"); if (checkmark) checkmark.remove(); if (li.dataset.value === value) { li.setAttribute("aria-selected", "true"); const checkMarkSpan = document.createElement("span"); checkMarkSpan.className = "absolute inset-y-0 right-0 flex items-center pr-4 ctp-text-lavender"; checkMarkSpan.innerHTML = ` `; li.appendChild(checkMarkSpan); } else { li.setAttribute("aria-selected", "false"); } }); toggleDropdown(); // Dispatch change event without bubbling const changeEvent = new Event("change", { bubbles: false }); hiddenInput.dispatchEvent(changeEvent); // Push the value to the LiveView this.pushEventTo(this.el, "change", { value: value }); }; // Event Listeners trigger.addEventListener("click", (e) => { e.preventDefault(); e.stopPropagation(); toggleDropdown(e); }); search.addEventListener("input", (e) => { e.preventDefault(); e.stopPropagation(); filterOptions(e.target.value); }); options.querySelectorAll("li").forEach((option) => { option.addEventListener("click", (e) => { e.preventDefault(); e.stopPropagation(); selectOption(option, e); }); }); // Prevent dropdowns from causing form submission container.addEventListener("submit", (e) => { e.preventDefault(); e.stopPropagation(); return false; }); // Close dropdown when clicking outside document.addEventListener("click", (e) => { if (!container.contains(e.target)) { dropdown.classList.add("hidden"); dropdown.classList.remove("block"); } }); // Keyboard navigation container.addEventListener("keydown", (e) => { const items = Array.from(options.querySelectorAll("li")).filter( (li) => li.style.display !== "none", ); const currentIdx = items.findIndex((li) => li === document.activeElement); switch (e.key) { case "ArrowDown": e.preventDefault(); if (currentIdx < items.length - 1) { items[currentIdx + 1].focus(); } break; case "ArrowUp": e.preventDefault(); if (currentIdx > 0) { items[currentIdx - 1].focus(); } break; case "Enter": e.preventDefault(); if (document.activeElement.tagName === "LI") { selectOption(document.activeElement, e); } break; case "Escape": e.preventDefault(); toggleDropdown(e); break; } }); // Prevent the dropdown from closing when clicking inside it dropdown.addEventListener("click", (e) => { e.stopPropagation(); }); // Prevent the search input from submitting the form search.addEventListener("keydown", (e) => { if (e.key === "Enter") { e.preventDefault(); e.stopPropagation(); return false; } }); }, }; export { SelectHook };