1/** 2 * Script JavaScript pour le bloc ACF Tabbed Content 3 * Gère le changement d'onglets avec transitions et navigation au clavier 4 */ 5 6(function () { 7 'use strict'; 8 9 /** 10 * Initialiser un bloc de contenu à onglets spécifique 11 */ 12 function initTabbedContent(blockElement) { 13 const panels = blockElement.querySelectorAll('.tabbed-content__panel'); 14 15 if (panels.length === 0) { 16 console.warn('Aucun panneau trouvé dans le bloc'); 17 return; 18 } 19 20 // Nombre total d'onglets (basé sur les panneaux) 21 const totalTabs = panels.length; 22 23 /** 24 * Activer un onglet spécifique 25 */ 26 function activateTab(targetIndex) { 27 // Record if focus was inside the block to restore it correctly 28 const currentFocus = document.activeElement; 29 const focusWasOnTab = currentFocus && currentFocus.classList.contains('tabbed-content__tab'); 30 31 // Mettre à jour tous les panneaux 32 panels.forEach(function (panel, panelIndex) { 33 const isTargetPanel = panelIndex === targetIndex; 34 35 // Hard switching - instant change without transitions 36 if (isTargetPanel) { 37 panel.classList.add('is-active'); 38 panel.removeAttribute('hidden'); 39 panel.setAttribute('aria-hidden', 'false'); 40 } else { 41 panel.classList.remove('is-active'); 42 panel.setAttribute('aria-hidden', 'true'); 43 } 44 45 // Mettre à jour tous les boutons d'onglet dans ce panneau 46 const tabButtons = panel.querySelectorAll('.tabbed-content__tab'); 47 tabButtons.forEach(function (tab, tabIndex) { 48 const isActiveTab = tabIndex === targetIndex; 49 50 tab.classList.toggle('is-active', isActiveTab); 51 tab.setAttribute('aria-selected', isActiveTab ? 'true' : 'false'); 52 // On ne modifie plus tabindex ici pour permettre le tab focus sur tous les onglets 53 }); 54 }); 55 56 // Si le focus était sur un onglet, le déplacer vers le même onglet dans le nouveau panneau actif 57 if (focusWasOnTab) { 58 const newActivePanel = blockElement.querySelector('.tabbed-content__panel.is-active'); 59 if (newActivePanel) { 60 const newTabButton = newActivePanel.querySelector('.tabbed-content__tab[data-tab-index="' + targetIndex + '"]'); 61 if (newTabButton) { 62 newTabButton.focus(); 63 } 64 } 65 } 66 67 // Ãvénement personnalisé 68 const event = new CustomEvent('tabbed-content-changed', { 69 detail: { 70 block: blockElement, 71 activeIndex: targetIndex 72 } 73 }); 74 blockElement.dispatchEvent(event); 75 } 76 77 /** 78 * Gérer les clics sur les onglets 79 */ 80 panels.forEach(function (panel) { 81 const tabButtons = panel.querySelectorAll('.tabbed-content__tab'); 82 83 tabButtons.forEach(function (tab) { 84 // Ãviter d'ajouter plusieurs event listeners 85 if (tab.dataset.initialized) { 86 return; 87 } 88 tab.dataset.initialized = 'true'; 89 90 tab.addEventListener('click', function (e) { 91 e.preventDefault(); 92 const targetIndex = parseInt(tab.dataset.tabIndex, 10); 93 activateTab(targetIndex); 94 }); 95 96 /** 97 * Gérer la navigation au clavier 98 */ 99 tab.addEventListener('keydown', function (e) { 100 const currentIndex = parseInt(tab.dataset.tabIndex, 10); 101 let newIndex = -1; 102 103 switch (e.key) { 104 case 'ArrowLeft': 105 case 'ArrowUp': 106 e.preventDefault(); 107 newIndex = currentIndex > 0 ? currentIndex - 1 : totalTabs - 1; 108 break; 109 110 case 'ArrowRight': 111 case 'ArrowDown': 112 e.preventDefault(); 113 newIndex = currentIndex < totalTabs - 1 ? currentIndex + 1 : 0; 114 break; 115 116 case 'Home': 117 e.preventDefault(); 118 newIndex = 0; 119 break; 120 121 case 'End': 122 e.preventDefault(); 123 newIndex = totalTabs - 1; 124 break; 125 126 case 'Enter': 127 case ' ': 128 e.preventDefault(); 129 activateTab(currentIndex); 130 return; 131 } 132 133 if (newIndex !== -1) { 134 activateTab(newIndex); 135 // Focus is handled inside activateTab now 136 } 137 }); 138 }); 139 }); 140 141 // Marquer le bloc comme initialisé 142 blockElement.classList.add('is-initialized'); 143 144 // Ãvénement personnalisé d'initialisation 145 const initEvent = new CustomEvent('tabbed-content-initialized', { 146 detail: { 147 block: blockElement, 148 tabCount: totalTabs 149 } 150 }); 151 blockElement.dispatchEvent(initEvent); 152 } 153 154 /** 155 * Initialiser tous les blocs de contenu à onglets de la page 156 */ 157 function initAllTabbedContent() { 158 const blocks = document.querySelectorAll('.tabbed-content:not(.is-initialized)'); 159 160 if (blocks.length === 0) { 161 return; 162 } 163 164 blocks.forEach(function (block) { 165 initTabbedContent(block); 166 }); 167 } 168 169 /** 170 * Initialiser au chargement du DOM 171 */ 172 if (document.readyState === 'loading') { 173 document.addEventListener('DOMContentLoaded', initAllTabbedContent); 174 } else { 175 initAllTabbedContent(); 176 } 177 178 /** 179 * Ré-initialiser après le chargement complet de la page 180 */ 181 window.addEventListener('load', function () { 182 setTimeout(initAllTabbedContent, 100); 183 }); 184 185 /** 186 * MutationObserver â éditeur Gutenberg / ACF preview
187 * ACF injecte le HTML du bloc après le DOMContentLoaded, 188 * l'observer détecte chaque nouveau rendu et réinitialise. 189 */ 190 if (typeof MutationObserver !== 'undefined' && document.body) { 191 var editorObserver = new MutationObserver(function (mutations) { 192 var needsInit = false; 193 mutations.forEach(function (mutation) { 194 mutation.addedNodes.forEach(function (node) { 195 if (node.nodeType !== 1) return; 196 if ( 197 (node.classList && node.classList.contains('tabbed-content')) || 198 (node.querySelector && node.querySelector('.tabbed-content')) 199 ) { 200 needsInit = true; 201 } 202 }); 203 }); 204 if (needsInit) { 205 initAllTabbedContent(); 206 } 207 }); 208 209 editorObserver.observe(document.body, { childList: true, subtree: true }); 210 } 211 212 /** 213 * API publique pour initialiser manuellement un bloc 214 */ 215 window['orion-blocksInitTabbedContent'] = function (element) { 216 if (!element) { 217 console.error('Ãlément tabbed content non fourni'); 218 return; 219 } 220 221 // Retirer la classe d'initialisation si présente 222 element.classList.remove('is-initialized'); 223 224 // Initialiser 225 initTabbedContent(element); 226 }; 227 228 /** 229 * API publique pour activer un onglet spécifique 230 */ 231 window['orion-blocksActivateTab'] = function (blockElement, tabIndex) { 232 if (!blockElement) { 233 console.error('Ãlément tabbed content non fourni'); 234 return; 235 } 236 237 const tabs = blockElement.querySelectorAll('.tabbed-content__tab'); 238 239 if (tabIndex < 0 || tabIndex >= tabs.length) { 240 console.error('Index d\'onglet invalide:', tabIndex); 241 return; 242 } 243 244 // Simuler un clic sur l'onglet 245 tabs[tabIndex].click(); 246 }; 247 248})(); 249
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.