Menus de navigation

Sur cette page

Les menus EmDash sont des listes ordonnées de liens gérées dans l’interface d’administration. Ils prennent en charge l’imbrication pour les menus déroulants et peuvent pointer vers des pages, articles, termes de taxonomie ou URL externes.

Interroger les menus

Utilisez getMenu() pour récupérer un menu par son nom unique :

---
import { getMenu } from "emdash";

const primaryMenu = await getMenu("primary");
---

{primaryMenu && (
  <nav>
    <ul>
      {primaryMenu.items.map(item => (
        <li>
          <a href={item.url}>{item.label}</a>
        </li>
      ))}
    </ul>
  </nav>
)}

La fonction renvoie null si aucun menu ne porte ce nom.

Structure d’un menu

Un menu contient des métadonnées et un tableau d’éléments :

interface Menu {
	id: string;
	name: string; // Unique identifier ("primary", "footer")
	label: string; // Display name ("Primary Navigation")
	items: MenuItem[];
}

interface MenuItem {
	id: string;
	label: string;
	url: string; // Resolved URL
	target?: string; // "_blank" for new window
	titleAttr?: string; // HTML title attribute
	cssClasses?: string; // Custom CSS classes
	children: MenuItem[]; // Nested items for dropdowns
}

Les URL sont résolues automatiquement selon le type d’élément :

  • Éléments page/article/{collection}/{slug}
  • Éléments taxonomie/{taxonomy}/{slug}
  • Éléments collection/{collection}/
  • Liens personnalisés → URL telle quelle

Rendre des menus imbriqués

Les éléments peuvent avoir des enfants pour les menus déroulants. Gérez l’imbrication en rendant récursivement le tableau children :

---
import { getMenu } from "emdash";
import type { MenuItem } from "emdash";

interface Props {
  name: string;
}

const menu = await getMenu(Astro.props.name);
---

{menu && (
  <nav class="nav">
    <ul class="nav-list">
      {menu.items.map(item => (
        <li class:list={["nav-item", item.cssClasses]}>
          <a
            href={item.url}
            target={item.target}
            title={item.titleAttr}
            aria-current={Astro.url.pathname === item.url ? "page" : undefined}
          >
            {item.label}
          </a>
          {item.children.length > 0 && (
            <ul class="submenu">
              {item.children.map(child => (
                <li>
                  <a href={child.url} target={child.target}>
                    {child.label}
                  </a>
                </li>
              ))}
            </ul>
          )}
        </li>
      ))}
    </ul>
  </nav>
)}

Types d’éléments de menu

L’administration prend en charge cinq types :

TypeDescriptionRésolution d’URL
pageLien vers une page/{collection}/{slug}
postLien vers un article/{collection}/{slug}
taxonomyLien vers catégorie ou étiquette/{taxonomy}/{slug}
collectionLien vers archive de collection/{collection}/
customURL externe ou personnaliséeTelle quelle

Lister tous les menus

Utilisez getMenus() pour obtenir toutes les définitions de menus (sans éléments) :

import { getMenus } from "emdash";

const menus = await getMenus();
// Returns: [{ id, name, label }, ...]

Principalement utile pour des interfaces d’administration ou le débogage.

Créer des menus

Créez des menus dans l’admin à /_emdash/admin/menus ou via l’API d’administration :

POST /_emdash/api/menus
Content-Type: application/json

{
  "name": "footer",
  "label": "Footer Navigation"
}

Ajouter des éléments :

POST /_emdash/api/menus/footer/items
Content-Type: application/json

{
  "type": "page",
  "referenceCollection": "pages",
  "referenceId": "page_privacy",
  "label": "Privacy Policy"
}

Ajouter un lien externe personnalisé :

POST /_emdash/api/menus/footer/items
Content-Type: application/json

{
  "type": "custom",
  "customUrl": "https://github.com/example",
  "label": "GitHub",
  "target": "_blank"
}

Réordonner et imbriquer

Mettez à jour l’ordre et la relation parent-enfant via le point de terminaison de réordonnancement :

POST /_emdash/api/menus/primary/reorder
Content-Type: application/json

{
  "items": [
    { "id": "item_1", "parentId": null, "sortOrder": 0 },
    { "id": "item_2", "parentId": null, "sortOrder": 1 },
    { "id": "item_3", "parentId": "item_2", "sortOrder": 0 }
  ]
}

Ainsi item_3 devient enfant de item_2, formant un menu déroulant.

Exemple complet

Exemple d’en-tête responsive avec navigation principale :

---
import { getMenu, getSiteSettings } from "emdash";

const settings = await getSiteSettings();
const primaryMenu = await getMenu("primary");
---

<html lang="en">
  <head>
    <title>{settings.title}</title>
  </head>
  <body>
    <header class="header">
      <a href="/" class="logo">
        {settings.logo ? (
          <img src={settings.logo.url} alt={settings.logo.alt || settings.title} />
        ) : (
          settings.title
        )}
      </a>

      {primaryMenu && (
        <nav class="main-nav" aria-label="Main navigation">
          <ul>
            {primaryMenu.items.map(item => (
              <li class:list={[item.cssClasses, { "has-children": item.children.length > 0 }]}>
                <a
                  href={item.url}
                  target={item.target}
                  aria-current={Astro.url.pathname === item.url ? "page" : undefined}
                >
                  {item.label}
                </a>
                {item.children.length > 0 && (
                  <ul class="dropdown">
                    {item.children.map(child => (
                      <li>
                        <a href={child.url} target={child.target}>{child.label}</a>
                      </li>
                    ))}
                  </ul>
                )}
              </li>
            ))}
          </ul>
        </nav>
      )}
    </header>

    <main>
      <slot />
    </main>
  </body>
</html>

Référence API

getMenu(name)

Récupère un menu par nom avec tous les éléments et URL résolues.

Paramètres :

  • name — Identifiant unique du menu (chaîne)

Retour : Promise<Menu | null>

getMenus()

Liste toutes les définitions de menus sans éléments.

Retour : Promise<Array<{ id: string; name: string; label: string }>>