Le menu Navigation des "livres"
Activer le module "book" ;
Créer au moins un livre et deux pages ;
Adapter (définir) les templates (gabarits) aux besoins du site ;
Adapter (définir) le style du menu.
1. Activer le module Book
Ce module faisant partie du cœur de Drupal, il suffit de l'activer (par le lien /admin/modules).
2. Créer un livre ou des pages d'un livre
Dans la page "ajouter un contenu", nous disposons dans la partie droite (cf : illustration ci-jointe) d'un formulaire permettant de manipuler la hiérarchie des livres.
Dans la zone déroulante, un clic sur "créer un nouveau livre" fait de la page en cours la racine d'un nouveau livre dont le nom sera le titre de la page.
3. Adapter les templates
Les templates "book-tree.html.twig" et "book-navigation.html.twig" sont les modèles utilisés dans "Drupal" pour l'affichage du menu de navigation dans un livre.
Ils se trouvent dorénavant dans le dossier "/core/themes/stable/templates/navigation".
Ils doivent être copiés dans le dossier "template" de votre thème pour être adaptés.
book-tree.html.twig rend l'affichage de la zone 1 de l'image figurant dans l'introduction de ce billet ;
book-navigation.html.twig rend l'affichage de la zone 2 ;
L'affichage de la zone 3 provient du plugin jQuery "tooltip" appliqué sur les éléments "title" des liens.
Les pages des livres de mon site étant construite avec le module 'Layout Builder', j'ai ajouté le bloc de menu "book navigation" dans un des blocs de mise en page dans mes types de contenu pour réaliser le menu de navigation des livres. C'est pourquoi, sur mon site, c'est le template "block--book-navigation.html.twig" qui produit l'affichage du menu. Le code de ce template est affiché dans l'onglet en bas de page, il est une adaptation des templates "book-tree" et "book-navigation" cités ci-dessus et étend le template "block.html.twig".
4. Adapter le style
Pour cela, il faut adapter (ou définir) des classes CSS. Dans mon installation, elles sont regroupées dans le fichier "nav-book.css".
{#
/**
* @file
* Theme override to display a book tree.
*
* Returns HTML for a wrapper for a book menu.
*
* Available variables:
* - items: A nested list of book items. Each book item contains:
* - attributes: HTML attributes for the book item.
* - below: The book item child items.
* - title: The book link title.
* - url: The book link URL, instance of \Drupal\Core\Url.
* - is_expanded: TRUE if the link has visible children within the current
* book tree.
* - is_collapsed: TRUE if the link has children within the current book tree
* that are not currently visible.
* - in_active_trail: TRUE if the link is in the active trail.
*/
{{ vardumper()}}
#}
{# dh modifié janvier 2025 #}
{% extends "block.html.twig" %}
{% import _self as mybook_tree %}
{# la ligne ci-dessus donne un alias à _self et permet de clarifier l'appel des macro
par exemple : mybook_tree.getListe() appellera la macro getListe ci-dessous
#}
{% macro getListe(level, elements) %}
{#
* affiche les éléments de la (sous)hiérarchie sous forme de liste
* level : la profondeur
* les éléments à afficher
#}
<nav class="book-navigation" role="navigation">
<div class="book">
<ul>
{% if level == 0 %}
<h3>Chapitres de cette section : </h3>
{% else %}
<h3>Sous-chapitres de cette section, (niveau -{{ level }}) </h3>
{% endif %}
{% for item in elements %}
<li {{ item.attributes }} >
{#{{ loop.index }} #}{{ link(item.title, item.url) }}
{% if item.below %}
>>> {{ mybook_tree.getListe( level + 1, item.below ) }}
{% endif %}
</li>
{% endfor %}
</ul>
</div>
</nav>
{% endmacro getListe %}
{% macro getNav(precedePage, encoursPage, suitPage) %}
{#
* affiche la barre de navigation
* <<<<< precedePage encoursPage >>>>>>>> suitePage
#}
<nav class="book-navigation" role="navigation">
<ul class="book-pager">
<li class="book-pager__item book-pager__item--previous">
{% if precedePage != "" %}
<<<< <br />{{ precedePage }}
{% endif %}
</li>
<li class="book-pager__item book-pager__item--center"> {{ encoursPage }} </li>
<li class="book-pager__item book-pager__item--next">
{% if suitPage != "" %}
>>>> <br /> {{ suitPage }}
{% endif %}
</li>
</ul>
</nav>
{% endmacro getNav %}
{% macro getPage (ref, pos ) %}
{#
* la ref de la page
* item : content['#items'][key]
* pos : precedante milieu suivante
* crée le lien vers la page
#}
{% if pos == 0 %}
{{ link(ref.title, ref.url, create_attribute({'title': "Aller à la page précédante "})) }}
{% elseif pos == 1 %}
{% set title = "retour en haut de page" %}
{{ link(title, ref.url, create_attribute({'title': "Retour en haut de page"})) }}
{% elseif pos == 2 %}
{{ link(ref.title, ref.url, create_attribute({'title': "Aller à la page suivante "})) }}
{% endif %}
{% endmacro getPage %}
{% block content %}
<div{{ content_attributes.addClass('content') }}>
<br /><hr />
{% set inActiveTrail = 0 %}
{% set inActiveTrailkey = 0 %}
{% set inActiveTrail2 = 0 %}
{% set inActiveTrailkey2 = 0 %}
{% set precedePage = "" %}
{% set suitPage = "" %}
{% set encoursPage = "" %}
{% set isChild = false %}
{#
* calcul de inActiveTrail et inActiveTrailkey
* 0 pas de lien actif pour le level 0
* sinon indice de loop et key
#}
{% for key, item in content['#items'] %}
{% if item.in_active_trail %}
{% set inActiveTrail = loop.index %}
{% set inActiveTrailkey = key %}
{% endif %}
{% endfor %}
{# inActiveTrail : {{ inActiveTrail }} : inActiveTrailkey : {{ inActiveTrailkey }} : {{ content['#items'][inActiveTrailkey].title }} <br/>#}
{#
* calcul de inActiveTrail2 et inActiveTrailkey2
* 0 pas de lien actif pour le level 1
* sinon indice de loop et key
#}
{% if inActiveTrail != 0 %}
{% for key, item in content['#items'][inActiveTrailkey].below %}
{% if item.in_active_trail %}
{% set inActiveTrail2 = loop.index %}
{% set inActiveTrailkey2 = key %}
{% endif %}
{% endfor %}
{% endif %}
{# inActiveTrail2 : {{ inActiveTrail2 }} : inActiveTrailkey2 : {{ inActiveTrailkey2 }} : {{ content['#items'][inActiveTrailkey].below[inActiveTrailkey2 ].title }} #}
{# menu racine #}
{% if inActiveTrail == 0 %}
{{ mybook_tree.getListe(inActiveTrail, content['#items']) }}
{# fin menu racine #}
{% else %}
{# menu level 0 du livre #}
{% for key, item in content['#items'] %}
{% if (loop.index == inActiveTrail -1 and precedePage == "") %}
{% set precedePage %}
{{ mybook_tree.getPage (content['#items'][key], 0) }}
{% endset %}
{% endif %}
{% if loop.index == inActiveTrail %}
{% set encoursPage %}
page {{ loop.index }} sur {{ loop.length}}
<br /><br />
{{ mybook_tree.getPage (content['#items'][key],1 ) }}
{% endset %}
{% endif %}
{% if (loop.index == inActiveTrail +1 and suitPage == "") %}
{% set suitPage %}
{{ mybook_tree.getPage (content['#items'][key], 2 ) }}
{% endset %}
{% endif %}
{% if item.below and item.in_active_trail %}
{# utilisation de inActiveTrail2 menu level 1 du livre #}
{% if inActiveTrail2 == 0 %}
{{ mybook_tree.getListe( 1, item.below) }}
{% else %}
{% for key, subitem in item.below %}
{% if loop.index == inActiveTrail2 -1 %}
{% set precedePage %}
{{ mybook_tree.getPage (item.below[key], "à la page précédente" ) }}
{% endset %}
{% endif %}
{% if precedePage == "" %}
{% set precedePage %}
{{ mybook_tree.getPage (item, 0 ) }}
{% endset %}
{% endif %}
{% if loop.index == inActiveTrail2 %}
{% set encoursPage %}
page {{ loop.index }} sur {{ loop.length}}
<br/>
{{ mybook_tree.getPage (content['#items'][inActiveTrailkey].below[key], 1 ) }}
<br/>
{% endset %}
{% endif %}
{% if loop.index == inActiveTrail2 +1 %}
{% set suitPage %}
{{ mybook_tree.getPage (item.below[key], 2 ) }}
{% endset %}
{% endif %}
{% if subitem.below and subitem.in_active_trail %}
{{ mybook_tree.getListe( 2, subitem.below) }}
{% endif %}
{% endfor %}
{% endif %}
{% endif %}
{% endfor %}
{{ mybook_tree.getNav(precedePage, encoursPage, suitPage) }}
{# fin menu level 0 du livre #}
{% endif %}
</div>
{% endblock %}