diff --git a/web/themes/custom/leshed/leshed.theme b/web/themes/custom/leshed/leshed.theme index 60235ec..4bdd519 100644 --- a/web/themes/custom/leshed/leshed.theme +++ b/web/themes/custom/leshed/leshed.theme @@ -147,6 +147,74 @@ function parse_menu_item(&$items, $key){ } } +/** + * Implements hook_preprocess_HOOK() for menu.html.twig. + * + * Burger (block leshed_navigationprincipale, menu 'main') : le lien actif + * passe en typo « variable title ». La classe est posée côté serveur pour + * être présente au premier rendu — titles.js la cherche à ce moment-là, et + * le CSS masque .variable-title jusqu'au split (pas de flash de typo brute). + * + * Détection du lien actif : on compare le chemin système courant au chemin + * interne de chaque lien (même logique que active-link.js / ActiveLinkResponse + * Filter, dont la classe `is-active` n'est dans le HTML que pour les réponses + * anonymes en cache). On ne peut PAS utiliser `in_active_trail` : les liens + * de menu vers des pages de vues ne matchent jamais — la route courante + * expose des paramètres (view_id, display_id, arg_0) absents du lien, donc + * loadLinksByRoute() ne le retrouve pas et le trail reste vide (mesuré). + */ +function leshed_preprocess_menu(&$variables) { + if (($variables['menu_name'] ?? '') !== 'main') { + return; + } + // La détection dépend du chemin courant, mais le bloc ne varie par défaut + // que par route.menu_active_trails:main — vide sur ce site à cause des + // liens views (cf. ci-dessous). Sans ce contexte, le bloc mis en cache sur + // une page resservirait sur une autre avec le mauvais lien marqué. + $variables['#cache']['contexts'][] = 'url.path'; + // isFrontPage() lit la config page.front : invalider si elle change. + $variables['#cache']['tags'][] = 'config:system.site'; + $current = \Drupal::service('path.current')->getPath(); + $is_front = \Drupal::service('path.matcher')->isFrontPage(); + leshed_menu_variable_title($variables['items'], $current, $is_front); +} + +/** + * Pose la classe .variable-title sur l'item actif (chemin système courant). + * + * Chemin interne normalisé avec un « / » initial : la route a un + * chemin interne vide. La page d'accueil est configurée sur un chemin + * système (page.front), donc sur la page d'accueil on marque l'item + * via isFrontPage() plutôt que par le chemin. + * + * Marque au plus UN item (le premier trouvé) : un lien et un lien + * pointant la page d'accueil configurée ne doivent pas être marqués ensemble. + * + * NB : $item est une copie du tableau, mais $item['url'] est un objet Url + * partagé — le setOptions() mute bien celui que rendra le template. + * + * @param string $current + * Chemin système courant (ex. « /projets », « / »). + * @param bool $is_front + * La page courante est la page d'accueil. + */ +function leshed_menu_variable_title(array $items, $current, $is_front) { + foreach ($items as $item) { + if (!empty($item['below']) && leshed_menu_variable_title($item['below'], $current, $is_front)) { + return TRUE; + } + $url = $item['url']; + if ($url->isRouted() + && (($is_front && $url->getRouteName() === '') || '/' . $url->getInternalPath() === $current)) { + $options = $url->getOptions(); + $options['attributes']['class'][] = 'variable-title'; + $url->setOptions($options); + return TRUE; + } + } + return FALSE; +} + /** * Implements hook_preprocess_HOOK() for node.html.twig. */