1import {Controller} from "@hotwired/stimulus"; 2 3export default class extends Controller { 4 static targets = ["questions", "dateDebutInput", "choiceIcon"]; 5 static values = { 6 prototypeQuestion: String, 7 }; 8 9 connect() { 10 this.updateQuestionIndices(); 11 this.initializeIcon(); 12 // Document.querySelector('.form_sondage').addEventListener('submit', (e)) 13 } 14 15 choiceIconTargetConnected(element) { 16 this.initializeIcon(element.closest('.choice-item')); 17 } 18 19 //Ajout d'une question via les boutons 20 addQuestion(event) { 21 event.preventDefault(); 22 const questionType = event.params.questionType || "reponse_courte"; 23 24 // Ãtape 1 : Ajouter la div 25 const newElement = this.createQuestionElement(); 26 27 if (!newElement) { 28 console.error("Le nouvel élément est nul. Vérifiez le prototype."); 29 return; 30 } 31 32 this.questionsTarget.appendChild(newElement); 33 34 // Ãtape 2 : Mettre le type selecteur de de question au bon type 35 this.setQuestionType(newElement, questionType); 36 37 // Ãtape 3 : Renuméroter 38 this.updateQuestionIndices(); 39 } 40 41 createQuestionElement() { 42 const tempDiv = document.createElement("div"); 43 tempDiv.innerHTML = this.prototypeQuestionValue; 44 return tempDiv.firstElementChild; 45 } 46 47 setQuestionType(element, questionType) { 48 const typeSelect = element.querySelector(".selection-type"); 49 if (typeSelect) { 50 for (let option of typeSelect.options) { 51 if (option.getAttribute("code") === questionType) { 52 typeSelect.value = option.value; 53 break; 54 } 55 } 56 this.updateQuestionDisplay(null, typeSelect); 57 } 58 } 59 60 updateQuestionDisplay(event = null, typeSelect = null) { 61 if (event != null) { 62 typeSelect = event.currentTarget; 63 } 64 const questionElement = typeSelect.closest('.question-item'); 65 const selectedOption = typeSelect.options[typeSelect.selectedIndex]; 66 const questionType = selectedOption.getAttribute("code"); 67 68 const choixContainer = questionElement.querySelector(".hide-choix"); 69 70 const requiresChoices = ["choix_simple", "choix_multiple"].includes(questionType); 71 72 if (choixContainer) { 73 if (requiresChoices) { 74 choixContainer.classList.remove("d-none"); 75 } else { 76 choixContainer.classList.add("d-none"); 77 } 78 } 79 80 // Mise à jour des icônes des choix 81 this.updateChoiceIcons(questionElement, questionType); 82 } 83 84 85 remove(event) { 86 event.preventDefault(); 87 const questionElement = event.currentTarget.closest('.question-item'); 88 89 questionElement.remove(); 90 91 // Renuméroter 92 this.updateQuestionIndices(); 93 } 94 95 moveUp(event) { 96 event.preventDefault(); 97 const questionElement = event.currentTarget.closest('.question-item'); 98 if (!questionElement) { 99 return; 100 } 101 102 const previousElement = questionElement.previousElementSibling; 103 if (previousElement && previousElement.classList.contains('question-item')) { 104 this.questionsTarget.insertBefore(questionElement, previousElement); 105 106 // Renuméroter 107 this.updateQuestionIndices(); 108 } 109 } 110 111 moveDown(event) { 112 event.preventDefault(); 113 const questionElement = event.currentTarget.closest('.question-item'); 114 if (!questionElement) { 115 return; 116 } 117 const nextElement = questionElement.nextElementSibling; 118 if (nextElement && nextElement.classList.contains('question-item')) { 119 this.questionsTarget.insertBefore(nextElement, questionElement); 120 121 // Renuméroter 122 this.updateQuestionIndices(); 123 } 124 } 125 126 duplicate(event) { 127 event.preventDefault(); 128 const questionElement = event.currentTarget.closest('.question-item'); 129 if (!questionElement) { 130 return; 131 } 132 133 // Copier la div 134 const clone = questionElement.cloneNode(true); 135 136 // Insérer le clone 137 questionElement.insertAdjacentElement('afterend', clone); 138 139 // Renuméroter 140 this.updateQuestionIndices(); 141 } 142 143 updateQuestionIndices() { 144 const questionElements = this.questionsTarget.querySelectorAll('.question-item'); 145
146 questionElements.forEach((questionElement, questionIndex) => { 147 // Mettre à jour l'affichage de l'ordre 148 const displayOrdre = questionElement.querySelector('.display-ordre'); 149 if (displayOrdre) { 150 displayOrdre.textContent = questionIndex + 1; 151 } 152 153 // Mettre à jour les indices dans les attributs name et id des champs de la question 154 this.updateElementIndices(questionElement, questionIndex); 155 156 // Mettre à jour l'indice dans l'input hidden 'ordre' de la question 157 const ordreInput = questionElement.querySelector('input[name$="[ordre]"]'); 158 if (ordreInput) { 159 ordreInput.value = questionIndex; 160 } 161 162 const choiceElements = questionElement.querySelectorAll('.choice-item'); 163 choiceElements.forEach((choiceElement, choiceIndex) => { 164 this.updateElementIndices(choiceElement, questionIndex, choiceIndex); 165 166 // Mettre à jour l'indice dans l'input hidden 'ordre' du choix 167 const ordreChoiceInput = choiceElement.querySelector('input[name$="[ordre]"]'); 168 if (ordreChoiceInput) { 169 ordreChoiceInput.value = choiceIndex; 170 } 171 }); 172 }); 173 } 174 175 updateElementIndices(element, questionIndex, choiceIndex = null) { 176 // Mettre à jour l'élément lui-même 177 this.updateAttributes(element, questionIndex, choiceIndex); 178 179 // Mettre à jour les descendants 180 element.querySelectorAll('[name], [id]').forEach((input) => { 181 this.updateAttributes(input, questionIndex, choiceIndex); 182 }); 183 } 184 185 updateAttributes(element, questionIndex, choiceIndex) { 186 let name = element.getAttribute('name'); 187 let id = element.getAttribute('id'); 188 let newId = ''; 189 190 // Remplacer les placeholders dans 'name' et 'id' 191 if (name) { 192 let newName = name.replaceAll(/\[questions\]\[(__QUESTION_PROTOTYPE__|\d+)\]/g, `[questions][${questionIndex}]`); 193 if (choiceIndex !== null) { 194 newName = newName.replaceAll(/\[choices\]\[(__CHOIX_PROTOTYPE__|\d+)\]/g, `[choices][${choiceIndex}]`); 195 } 196 element.setAttribute('name', newName); 197 } 198 199 if (id) { 200 newId = id.replaceAll(/_questions_(__QUESTION_PROTOTYPE__|\d+)/g, `_questions_${questionIndex}`); 201 if (choiceIndex !== null) { 202 newId = newId.replaceAll(/_choices_(__CHOIX_PROTOTYPE__|\d+)_/g, `_choices_${choiceIndex}`); 203 } 204 element.setAttribute('id', newId); 205 } 206 207 // Mettre à jour l'attribut 'for' des labels associés 208 if (id) { 209 const labels = element.closest('.question-item').querySelectorAll(`label[for="${id}"]`); 210 labels.forEach((label) => { 211 label.setAttribute('for', newId); 212 }); 213 } 214 } 215 216 toggleDateDebut(event) { 217 const input = this.dateDebutInputTarget.querySelector('input'); 218 input.required = event.target.checked; 219 if (event.target.checked) { 220 this.dateDebutInputTarget.classList.remove("d-none"); 221 } else { 222 this.dateDebutInputTarget.classList.add("d-none"); 223 input.value = ''; 224 } 225 } 226 227 228 // Méthode pour mettre à jour les icônes des choix en fonction du type de question 229 updateChoiceIcons(questionElement, questionType) { 230 const choiceIcons = questionElement.querySelectorAll('.icon-choix'); 231 choiceIcons.forEach(iconElement => { 232 this.changeIconeChoice(iconElement, questionType) 233 }); 234 } 235 236 changeIconeChoice(iconElement, questionType) { 237 iconElement.innerHTML = this.getIconHTML(questionType); 238 } 239 240 // Méthode pour initialiser les icônes de tous les choix existants lors du chargement 241 initializeIcon(choiceElement = null) { 242 if (choiceElement) { 243 const questionElement = choiceElement.closest('.question-item'); 244 const typeSelect = questionElement.querySelector('.selection-type'); 245 const questionType = typeSelect ? typeSelect.options[typeSelect.selectedIndex].getAttribute('co
245de') : null; 246 if (questionType) { 247 const iconElement = choiceElement.querySelector('[data-sondage-form-target="choiceIcon"]'); 248 if (iconElement) { 249 this.changeIconeChoice(iconElement, questionType); 250 } 251 } 252 } else { 253 this.choiceIconTargets.forEach(iconElement => { 254 const choiceElement = iconElement.closest('.choice-item'); 255 const questionElement = choiceElement.closest('.question-item'); 256 const typeSelect = questionElement.querySelector('.selection-type'); 257 const questionType = typeSelect ? typeSelect.options[typeSelect.selectedIndex].getAttribute('code') : null; 258 console.log(typeSelect) 259 if (questionType) { 260 this.changeIconeChoice(iconElement, questionType); 261 } 262 }); 263 } 264 } 265 266 getIconHTML(type) { 267 const icons = { 268 'choix_simple': '<i class="fa-regular fa-circle-dot fs-3"></i>', 269 'choix_multiple': '<i class="fa-regular fa-square-check fs-3"></i>', 270 }; 271 return icons[type] || ''; 272 } 273 274 onSubmit(event) { 275 event.preventDefault(); 276 277 const questionElements = this.questionsTarget.querySelectorAll('.question-item'); 278 279 questionElements.forEach(questionElement => { 280 const typeSelect = questionElement.querySelector('.selection-type'); 281 const questionType = typeSelect ? typeSelect.options[typeSelect.selectedIndex].getAttribute('code') : null; 282 283 if (!["choix_simple", "choix_multiple"].includes(questionType)) { 284 questionElement.querySelectorAll('.choix-item').forEach(choice => { 285 choice.remove(); 286 }) 287 } 288 }); 289 if (typeof this.element.requestSubmit === 'function') { 290 this.element.requestSubmit(); 291 } else { 292 this.element.submit(); 293 } 294 } 295}
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.