From fb78962fc1549f2df81c5d0d93d8bf3830ab4155 Mon Sep 17 00:00:00 2001 From: KONE SOREL Date: Mon, 22 Dec 2025 09:07:01 +0000 Subject: [PATCH] test --- Bootstrap_new/css/ux_enhancements.css | 147 ++++++++++++++++++++++++ Bootstrap_new/js/ux-manager.js | 157 ++++++++++++++++++-------- Vue/gabarit.php | 144 ++++++++++++++++++++++- 3 files changed, 394 insertions(+), 54 deletions(-) diff --git a/Bootstrap_new/css/ux_enhancements.css b/Bootstrap_new/css/ux_enhancements.css index 7a2cbdb..c02a379 100644 --- a/Bootstrap_new/css/ux_enhancements.css +++ b/Bootstrap_new/css/ux_enhancements.css @@ -135,4 +135,151 @@ html { @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } +} + +/* ============================================ + MENU UNIQUE OUVERT - STYLES AMÉLIORÉS + ============================================ */ + +/* Animation pour les sous-menus */ +@keyframes slideDown { + from { + opacity: 0; + transform: translateY(-10px); + max-height: 0; + } + to { + opacity: 1; + transform: translateY(0); + max-height: 500px; + } +} + +@keyframes slideUp { + from { + opacity: 1; + transform: translateY(0); + max-height: 500px; + } + to { + opacity: 0; + transform: translateY(-10px); + max-height: 0; + } +} + +/* Styles pour les sous-menus */ +.nav-submenu { + max-height: 0; + overflow: hidden; + display: none; + transition: max-height 0.3s ease, opacity 0.3s ease; +} + +.nav-submenu.show { + display: block; + animation: slideDown 0.3s ease forwards; +} + +.nav-submenu:not(.show) { + animation: slideUp 0.3s ease forwards; +} + +/* Indicateur visuel pour le menu actif */ +.nav-link.active { + background-color: rgba(255, 255, 255, 0.15) !important; + color: white !important; + font-weight: 500 !important; + position: relative; +} + +.nav-link.active::after { + content: ''; + position: absolute; + left: 0; + top: 50%; + transform: translateY(-50%); + width: 3px; + height: 60%; + background-color: white; + border-radius: 0 2px 2px 0; +} + +/* Flèche du menu */ +.nav-arrow { + transition: transform 0.3s ease; + margin-left: auto; +} + +.nav-link[aria-expanded="true"] .nav-arrow { + transform: rotate(90deg); +} + +.nav-link[aria-expanded="false"] .nav-arrow { + transform: rotate(0deg); +} + +/* Hover amélioré */ +.nav-link:hover:not(.active) { + background-color: rgba(255, 255, 255, 0.08) !important; +} + +/* Transition fluide pour les sous-menus actifs */ +.nav-submenu[data-parent-id].show { + background-color: rgba(0, 0, 0, 0.1); + border-radius: 6px; + margin: 4px 16px 4px 16px; +} + +/* Élément actif dans le sous-menu */ +.nav-submenu .nav-link.active { + background-color: rgba(255, 255, 255, 0.2) !important; + font-weight: 600 !important; +} + +/* ============================================ + RESPONSIVE - MENU MOBILE + ============================================ */ + +@media (max-width: 768px) { + .nav-submenu.show { + animation: slideDown 0.2s ease forwards; + } + + .nav-submenu:not(.show) { + animation: slideUp 0.2s ease forwards; + } + + /* Masquer les sous-menus par défaut sur mobile */ + .app-sidebar:not(.mobile-open) .nav-submenu { + display: none !important; + } + + .app-sidebar.mobile-open .nav-submenu.show { + display: block !important; + } +} + +/* ============================================ + ACCESSIBILITÉ AMÉLIORÉE + ============================================ */ + +.nav-link:focus { + outline: 2px solid rgba(255, 255, 255, 0.6); + outline-offset: -2px; +} + +.nav-submenu .nav-link:focus { + outline-offset: -4px; +} + +/* Focus visible pour navigation clavier */ +.nav-link:focus-visible { + outline: 3px solid rgba(255, 255, 255, 0.8); + outline-offset: 2px; +} + +/* Indicateur pour menu ouvert au clavier */ +.nav-link[aria-expanded="true"]:focus { + background-color: rgba(255, 255, 255, 0.2) !important; } \ No newline at end of file diff --git a/Bootstrap_new/js/ux-manager.js b/Bootstrap_new/js/ux-manager.js index d3b5361..0740453 100644 --- a/Bootstrap_new/js/ux-manager.js +++ b/Bootstrap_new/js/ux-manager.js @@ -81,60 +81,85 @@ class UXManager { } } -// Navigation Manager - Gestion des menus +// Navigation Manager - Gestion des menus (VERSION CORRIGÉE) class NavigationManager { constructor() { this.currentOpenMenu = null; + this.activeMenuId = null; // Menu actif basé sur la page courante } init() { + this.setupActiveMenu(); this.setupMenuBehavior(); this.setupKeyboardNavigation(); - this.setupActiveMenu(); + } + + setupActiveMenu() { + // Déterminer le menu actif basé sur la page courante + const activeParentId = window.appConfig?.activeParentId; + const activeLink = window.appConfig?.activeLink; + + console.log('[Navigation] Page active:', activeLink, 'Parent ID:', activeParentId); + + if (activeParentId !== null && activeParentId !== '') { + this.activeMenuId = `submenu${activeParentId}`; + console.log('[Navigation] Menu actif détecté:', this.activeMenuId); + + // Ouvrir seulement le menu actif + setTimeout(() => { + this.openMenu(this.activeMenuId); + this.currentOpenMenu = this.activeMenuId; + }, 100); + } else { + console.log('[Navigation] Aucun menu actif détecté'); + // Fermer tous les menus par défaut + this.closeAllMenus(); + } } setupMenuBehavior() { - // Désactiver le comportement Bootstrap par défaut + // Pour chaque lien de menu avec sous-menu document.querySelectorAll('.nav-link[data-bs-toggle="collapse"]').forEach(link => { link.addEventListener('click', (e) => { + e.preventDefault(); + e.stopPropagation(); + const targetId = link.getAttribute('href').substring(1); - this.toggleMenu(targetId, e); + console.log('[Navigation] Clic sur menu:', targetId); + + // Si on clique sur le menu déjà ouvert, on le ferme + if (this.currentOpenMenu === targetId) { + this.closeMenu(targetId); + this.currentOpenMenu = null; + } else { + // Fermer le menu précédent si différent + if (this.currentOpenMenu && this.currentOpenMenu !== targetId) { + this.closeMenu(this.currentOpenMenu); + } + + // Ouvrir le nouveau menu + this.openMenu(targetId); + this.currentOpenMenu = targetId; + } }); }); - // Fermer le menu en cliquant ailleurs + // Fermer les menus en cliquant ailleurs document.addEventListener('click', (e) => { if (!e.target.closest('.app-sidebar')) { - this.closeAllMenus(); + // Ne pas fermer le menu actif s'il correspond à la page courante + if (this.currentOpenMenu !== this.activeMenuId) { + this.closeAllMenus(); + } } }); - } - - toggleMenu(menuId, event) { - if (event) { - event.preventDefault(); - event.stopPropagation(); - } - const menu = document.getElementById(menuId); - const link = document.querySelector(`[href="#${menuId}"]`); - - if (!menu || !link) return; - - // Si ce menu est déjà ouvert, le fermer - if (this.currentOpenMenu === menuId) { - this.closeMenu(menuId); - this.currentOpenMenu = null; - } else { - // Fermer le menu actuellement ouvert - if (this.currentOpenMenu) { - this.closeMenu(this.currentOpenMenu); - } - - // Ouvrir le nouveau menu - this.openMenu(menuId); - this.currentOpenMenu = menuId; - } + // Empêcher la fermeture du menu actif + document.querySelectorAll('.nav-submenu').forEach(menu => { + menu.addEventListener('click', (e) => { + e.stopPropagation(); + }); + }); } openMenu(menuId) { @@ -142,10 +167,29 @@ class NavigationManager { const link = document.querySelector(`[href="#${menuId}"]`); if (menu && link) { + // Retirer 'show' de tous les autres menus + document.querySelectorAll('.nav-submenu.show').forEach(otherMenu => { + if (otherMenu.id !== menuId) { + otherMenu.classList.remove('show'); + const otherLink = document.querySelector(`[href="#${otherMenu.id}"]`); + if (otherLink) { + otherLink.setAttribute('aria-expanded', 'false'); + otherLink.classList.remove('active'); + const arrow = otherLink.querySelector('.nav-arrow'); + if (arrow) arrow.style.transform = 'rotate(0deg)'; + } + } + }); + + // Ouvrir le menu sélectionné menu.classList.add('show'); - link.classList.add('active'); link.setAttribute('aria-expanded', 'true'); - link.querySelector('.nav-arrow').style.transform = 'rotate(90deg)'; + link.classList.add('active'); + + const arrow = link.querySelector('.nav-arrow'); + if (arrow) arrow.style.transform = 'rotate(90deg)'; + + console.log('[Navigation] Menu ouvert:', menuId); } } @@ -154,19 +198,35 @@ class NavigationManager { const link = document.querySelector(`[href="#${menuId}"]`); if (menu && link) { + // Ne pas fermer le menu actif si c'est celui de la page courante + if (menuId === this.activeMenuId) { + console.log('[Navigation] Menu actif, ne pas fermer:', menuId); + return; + } + menu.classList.remove('show'); - link.classList.remove('active'); link.setAttribute('aria-expanded', 'false'); - link.querySelector('.nav-arrow').style.transform = 'rotate(0deg)'; + link.classList.remove('active'); + + const arrow = link.querySelector('.nav-arrow'); + if (arrow) arrow.style.transform = 'rotate(0deg)'; + + console.log('[Navigation] Menu fermé:', menuId); } } closeAllMenus() { + // Ne fermer que les menus qui ne sont pas actifs document.querySelectorAll('.nav-submenu.show').forEach(menu => { - const menuId = menu.id; - this.closeMenu(menuId); + if (menu.id !== this.activeMenuId) { + this.closeMenu(menu.id); + } }); - this.currentOpenMenu = null; + + // Si aucun menu actif, réinitialiser + if (!this.activeMenuId) { + this.currentOpenMenu = null; + } } setupKeyboardNavigation() { @@ -197,16 +257,15 @@ class NavigationManager { } } - setupActiveMenu() { - // Ouvrir automatiquement le menu actif au chargement - const activeParentId = window.appConfig.activeParentId; - if (activeParentId !== null && activeParentId !== '') { - const menuId = `submenu${activeParentId}`; - setTimeout(() => { - this.openMenu(menuId); - this.currentOpenMenu = menuId; - }, 100); - } + // Méthode publique pour forcer l'ouverture d'un menu + openMenuById(menuId) { + this.openMenu(menuId); + this.currentOpenMenu = menuId; + } + + // Méthode publique pour forcer la fermeture + closeAllExceptActive() { + this.closeAllMenus(); } } diff --git a/Vue/gabarit.php b/Vue/gabarit.php index 410f9ad..9391252 100755 --- a/Vue/gabarit.php +++ b/Vue/gabarit.php @@ -294,8 +294,8 @@ foreach ($menus as $key0 => $menuParent) { } } - // Déterminer si le menu doit être ouvert - $shouldBeOpen = $isParentActive || $hasActiveChild; + // Déterminer si le menu DOIT être ouvert (seulement si actif) + $shouldBeOpen = ($isParentActive || $hasActiveChild) && $activeParentId !== null; ?>