This commit is contained in:
KONE SOREL 2025-12-22 09:07:01 +00:00
parent 094929a2db
commit fb78962fc1
3 changed files with 394 additions and 54 deletions

View File

@ -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;
}

View File

@ -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();
}
}

View File

@ -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;
?>
<div class="nav-item">
<?php if (sizeof($menuChildrenLevelOne) > 0): ?>
@ -304,14 +304,16 @@ foreach ($menus as $key0 => $menuParent) {
data-bs-toggle="collapse"
onclick="appNavigation.toggleMenu('submenu<?= $key0 ?>', event)"
aria-expanded="<?= $shouldBeOpen ? 'true' : 'false' ?>"
aria-controls="submenu<?= $key0 ?>">
aria-controls="submenu<?= $key0 ?>"
data-menu-id="submenu<?= $key0 ?>">
<i class="<?= $menuParent['icone'] ?>"></i>
<span class="nav-text"><?= $menuParent['libeleMenu'] ?></span>
<i class="nav-arrow bi bi-chevron-right"></i>
</a>
<div class="nav-submenu collapse <?= $shouldBeOpen ? 'show' : '' ?>"
id="submenu<?= $key0 ?>">
id="submenu<?= $key0 ?>"
data-parent-id="submenu<?= $key0 ?>">
<?php foreach ($menuChildrenLevelOne as $key1 => $menuChild):
$childActive = (explode('/', $menuChild['lienMenu'])[0] ?? '') == $activeLink;
?>
@ -553,4 +555,136 @@ foreach ($menus as $key0 => $menuParent) {
<!-- Service Worker Registration -->
<script src="/Js/sw-register.js?ver=2025.12.22.00"></script>
</body>
</html>
</html>
<script>
// Test du système de menus
document.addEventListener('DOMContentLoaded', function() {
console.group('=== TEST SYSTÈME DE MENUS ===');
// Vérifier l'état initial
const activeMenuId = window.appConfig?.activeParentId !== null ?
`submenu${window.appConfig.activeParentId}` : null;
console.log('Menu actif configuré:', activeMenuId);
console.log('Page active:', window.appConfig?.activeLink);
// Compter les menus ouverts
const openMenus = document.querySelectorAll('.nav-submenu.show');
console.log('Menus initialement ouverts:', openMenus.length);
openMenus.forEach(menu => {
console.log(' -', menu.id);
});
// Vérifier que seul le menu actif est ouvert
if (openMenus.length > 1) {
console.warn('⚠️ Plusieurs menus sont ouverts!');
console.warn('Seul le menu', activeMenuId, 'devrait être ouvert.');
// Corriger automatiquement
if (activeMenuId && window.appNavigation) {
console.log('Correction automatique en cours...');
window.appNavigation.closeAllExceptActive();
}
} else if (openMenus.length === 1 && openMenus[0].id === activeMenuId) {
console.log('✅ Parfait! Seul le menu actif est ouvert.');
} else if (openMenus.length === 0 && !activeMenuId) {
console.log('✅ Aucun menu ouvert - Comportement attendu.');
}
// Exposer des fonctions de test
window.menuTest = {
openAllMenus: function() {
document.querySelectorAll('.nav-submenu').forEach(menu => {
menu.classList.add('show');
const link = document.querySelector(`[href="#${menu.id}"]`);
if (link) link.setAttribute('aria-expanded', 'true');
});
console.log('Tous les menus ouverts (test)');
},
closeAllMenus: function() {
document.querySelectorAll('.nav-submenu').forEach(menu => {
menu.classList.remove('show');
const link = document.querySelector(`[href="#${menu.id}"]`);
if (link) link.setAttribute('aria-expanded', 'false');
});
console.log('Tous les menus fermés (test)');
},
showMenuState: function() {
const menus = document.querySelectorAll('.nav-submenu');
console.log('État des menus:');
menus.forEach(menu => {
const isOpen = menu.classList.contains('show');
const link = document.querySelector(`[href="#${menu.id}"]`);
const ariaExpanded = link ? link.getAttribute('aria-expanded') : 'N/A';
console.log(` ${menu.id}: ${isOpen ? 'OUVERT' : 'FERMÉ'} (aria-expanded: ${ariaExpanded})`);
});
}
};
console.groupEnd();
// Ajouter un indicateur visuel de débogage
if (window.appConfig?.debugMode) {
const debugIndicator = document.createElement('div');
debugIndicator.style.cssText = `
position: fixed;
bottom: 10px;
left: 10px;
background: rgba(0,0,0,0.8);
color: #0f0;
padding: 5px 10px;
border-radius: 4px;
font-family: monospace;
font-size: 12px;
z-index: 9999;
pointer-events: none;
`;
debugIndicator.textContent = `Menu: ${activeMenuId || 'Aucun'}`;
document.body.appendChild(debugIndicator);
}
});
// Fonction globale pour tester depuis la console
function testMenuSystem() {
console.log('=== TEST MANUEL DU SYSTÈME DE MENUS ===');
if (!window.appNavigation) {
console.error('appNavigation non disponible');
return;
}
// Test 1: Fermer tous les menus
console.log('Test 1: Fermeture de tous les menus...');
window.appNavigation.closeAllMenus();
// Test 2: Ouvrir le menu actif
setTimeout(() => {
const activeId = window.appConfig?.activeParentId !== null ?
`submenu${window.appConfig.activeParentId}` : null;
if (activeId) {
console.log('Test 2: Ouverture du menu actif:', activeId);
window.appNavigation.openMenuById(activeId);
}
// Test 3: Vérification finale
setTimeout(() => {
const openMenus = document.querySelectorAll('.nav-submenu.show').length;
console.log('Test 3: Résultat -', openMenus, 'menu(s) ouvert(s)');
if (openMenus === 1 || (openMenus === 0 && !activeId)) {
console.log('✅ Système de menus fonctionne correctement!');
} else {
console.warn('⚠️ Problème détecté avec le système de menus');
}
}, 500);
}, 500);
}
// Exposer la fonction de test
window.testMenuSystem = testMenuSystem;
</script>