1import { Controller } from "@hotwired/stimulus"; 2 3export default class DropdownController extends Controller { 4 static targets = ["selectedItems", "checkbox", "hiddenInput", "menu"]; 5 6 connect() { 7 this.showPlaceholder(); 8 this.updateSelectedItems(); 9 document.addEventListener("click", this.outsideClick.bind(this)); 10 } 11 12 toggleDropdown() { 13 // Check if menu is currently hidden (either by CSS class or inline style) 14 const isHidden = this.menuTarget.classList.contains('hidden') || 15 this.menuTarget.style.display === 'none' || 16 !this.menuTarget.style.display; 17 18 if (isHidden) { 19 this.menuTarget.classList.remove('hidden'); 20 this.menuTarget.style.display = 'block'; 21 } else { 22 this.menuTarget.classList.add('hidden'); 23 this.menuTarget.style.display = 'none'; 24 } 25 } 26 27 outsideClick(event) { 28 if (!this.element.contains(event.target)) { 29 this.menuTarget.classList.add('hidden'); 30 this.menuTarget.style.display = 'none'; 31 } 32 } 33 34 updateSelectedItems() { 35 const selected = this.checkboxTargets 36 .filter((cb) => cb.checked) 37 .map((cb) => cb.value); 38 39 this.hiddenInputTarget.value = selected.join(","); 40 41 if (selected.length === 0) { 42 this.selectedItemsTarget.innerHTML = ""; 43 this.showPlaceholder(); 44 return; 45 } 46 47 if (this.selectedItemsTarget.dataset.placeholder === "true") { 48 this.selectedItemsTarget.innerHTML = ""; 49 delete this.selectedItemsTarget.dataset.placeholder; 50 } 51 52 this.selectedItemsTarget.querySelectorAll("[data-value]").forEach((badge) => { 53 if (!selected.includes(badge.dataset.value)) { 54 badge.remove(); 55 } 56 }); 57 58 selected.forEach((value) => { 59 if (this.selectedItemsTarget.querySelector(`[data-value="${value}"]`)) { 60 return; 61 } 62 63 const span = document.createElement("span"); 64 span.classList.add( 65 "bg-primary", 66 "text-white", 67 "px-2", 68 "py-1", 69 "flex", 70 "items-center", 71 "gap-1", 72 "rounded-md", 73 "text-md" 74 ); 75 span.dataset.value = value; 76 span.dataset.testid = "related-course-names"; 77 span.textContent = value; 78 79 const removeBtn = document.createElement("button"); 80 removeBtn.type = "button"; 81 removeBtn.textContent = "â"; 82 removeBtn.classList.add( 83 "text-white", 84 "bg-transparent", 85 "border-0", 86 "text-lg", 87 "cursor-pointer", 88 "ml-1" 89 ); 90 removeBtn.addEventListener("click", () => { 91 const checkbox = this.checkboxTargets.find((c) => c.value === value); 92 if (checkbox) checkbox.checked = false; 93 span.remove(); 94 this.updateSelectedItems(); 95 }); 96 97 span.appendChild(removeBtn); 98 this.selectedItemsTarget.appendChild(span); 99 }); 100 } 101 102 showPlaceholder() { 103 this.selectedItemsTarget.textContent = "Select related courses..."; 104 this.selectedItemsTarget.classList.add("rounded-lg", "text-gray-500"); 105 this.selectedItemsTarget.dataset.placeholder = "true"; 106 } 107}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.