Zones de widgets

Sur cette page

Les zones de widgets sont des régions nommées dans vos modèles où les administrateurs peuvent placer des blocs de contenu. Servez-vous-en pour les barres latérales, colonnes de pied de page, bannières promotionnelles ou toute section que les éditeurs doivent contrôler sans toucher au code.

Interroger les zones de widgets

Utilisez getWidgetArea() pour récupérer une zone de widgets par son nom :

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

const sidebar = await getWidgetArea("sidebar");
---

{sidebar && sidebar.widgets.length > 0 && (
  <aside class="sidebar">
    {sidebar.widgets.map(widget => (
      <div class="widget">
        {widget.title && <h3>{widget.title}</h3>}
        <!-- Rendu du contenu du widget -->
      </div>
    ))}
  </aside>
)}

La fonction renvoie null si la zone n’existe pas.

Structure d’une zone de widgets

Une zone de widgets contient des métadonnées et un tableau de widgets :

interface WidgetArea {
	id: string;
	name: string; // Identifiant unique ("sidebar", "footer-1")
	label: string; // Libellé affiché ("Barre latérale principale")
	description?: string;
	widgets: Widget[];
}

interface Widget {
	id: string;
	type: "content" | "menu" | "component";
	title?: string;
	// Champs spécifiques au type
	content?: PortableTextBlock[]; // Widgets de contenu
	menuName?: string; // Widgets de menu
	componentId?: string; // Widgets de composant
	componentProps?: Record<string, unknown>;
}

Types de widgets

EmDash prend en charge trois types de widgets :

Widgets de contenu

Texte enrichi stocké en Portable Text. Rendez-le avec le composant PortableText :

---
import { PortableText } from "emdash/ui";
---

{widget.type === "content" && widget.content && (
  <div class="widget-content">
    <PortableText value={widget.content} />
  </div>
)}

Widgets de menu

Affichez un menu de navigation dans une zone de widgets :

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

const menu = widget.menuName ? await getMenu(widget.menuName) : null;
---

{widget.type === "menu" && menu && (
  <nav class="widget-nav">
    <ul>
      {menu.items.map(item => (
        <li><a href={item.url}>{item.label}</a></li>
      ))}
    </ul>
  </nav>
)}

Widgets de composant

Rendez un composant enregistré avec des props configurables. EmDash inclut ces composants de base :

ID de composantDescriptionProps
core:recent-postsListe des articles récentscount, showThumbnails, showDate
core:categoriesListe des catégoriesshowCount, hierarchical
core:tagsNuage de tagsshowCount, limit
core:searchFormulaire de rechercheplaceholder
core:archivesArchives mensuelles/annuellestype, limit

Rendre les widgets

Créez un composant de rendu réutilisable :

---
import { PortableText } from "emdash/ui";
import { getMenu } from "emdash";
import type { Widget } from "emdash";

import RecentPosts from "./widgets/RecentPosts.astro";
import Categories from "./widgets/Categories.astro";
import TagCloud from "./widgets/TagCloud.astro";
import SearchForm from "./widgets/SearchForm.astro";
import Archives from "./widgets/Archives.astro";

interface Props {
  widget: Widget;
}

const { widget } = Astro.props;

const componentMap: Record<string, any> = {
  "core:recent-posts": RecentPosts,
  "core:categories": Categories,
  "core:tags": TagCloud,
  "core:search": SearchForm,
  "core:archives": Archives,
};

const menu = widget.type === "menu" && widget.menuName
  ? await getMenu(widget.menuName)
  : null;
---

<div class="widget">
  {widget.title && <h3 class="widget-title">{widget.title}</h3>}

  {widget.type === "content" && widget.content && (
    <div class="widget-content">
      <PortableText value={widget.content} />
    </div>
  )}

  {widget.type === "menu" && menu && (
    <nav class="widget-menu">
      <ul>
        {menu.items.map(item => (
          <li><a href={item.url}>{item.label}</a></li>
        ))}
      </ul>
    </nav>
  )}

  {widget.type === "component" && widget.componentId && componentMap[widget.componentId] && (
    <Fragment>
      {(() => {
        const Component = componentMap[widget.componentId!];
        return <Component {...widget.componentProps} />;
      })()}
    </Fragment>
  )}
</div>

Exemples de composants widget

Widget articles récents

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

interface Props {
  count?: number;
  showThumbnails?: boolean;
  showDate?: boolean;
}

const { count = 5, showThumbnails = false, showDate = true } = Astro.props;

const { entries: posts } = await getEmDashCollection("posts", {
  limit: count,
  orderBy: { publishedAt: "desc" },
});
---

<ul class="recent-posts">
  {posts.map(post => (
    <li>
      {showThumbnails && post.data.featured_image && (
        <img src={post.data.featured_image} alt="" class="thumbnail" />
      )}
      <a href={`/posts/${post.slug}`}>{post.data.title}</a>
      {showDate && post.data.publishedAt && (
        <time datetime={post.data.publishedAt.toISOString()}>
          {post.data.publishedAt.toLocaleDateString()}
        </time>
      )}
    </li>
  ))}
</ul>

Widget de recherche

---
interface Props {
  placeholder?: string;
}

const { placeholder = "Rechercher…" } = Astro.props;
---

<form action="/search" method="get" class="search-form">
  <input
    type="search"
    name="q"
    placeholder={placeholder}
    aria-label="Recherche"
  />
  <button type="submit">Rechercher</button>
</form>

Zones de widgets dans les layouts

Exemple de mise en page blog avec barre latérale :

---
import { getWidgetArea } from "emdash";
import WidgetRenderer from "../components/WidgetRenderer.astro";

const sidebar = await getWidgetArea("sidebar");
---

<div class="layout">
  <main class="content">
    <slot />
  </main>

  {sidebar && sidebar.widgets.length > 0 && (
    <aside class="sidebar">
      {sidebar.widgets.map(widget => (
        <WidgetRenderer widget={widget} />
      ))}
    </aside>
  )}
</div>

<style>
  .layout {
    display: grid;
    grid-template-columns: 1fr 300px;
    gap: 2rem;
  }

  @media (max-width: 768px) {
    .layout {
      grid-template-columns: 1fr;
    }
  }
</style>

Lister toutes les zones de widgets

Utilisez getWidgetAreas() pour récupérer toutes les zones avec leurs widgets :

import { getWidgetAreas } from "emdash";

const areas = await getWidgetAreas();
// Renvoie toutes les zones avec widgets peuplés

Créer des zones de widgets

Créez-les dans l’admin à /_emdash/admin/widgets, ou via l’API d’administration :

POST /_emdash/api/widget-areas
Content-Type: application/json

{
  "name": "footer-1",
  "label": "Pied de page colonne 1",
  "description": "Première colonne du pied de page"
}

Ajouter un widget de contenu :

POST /_emdash/api/widget-areas/footer-1/widgets
Content-Type: application/json

{
  "type": "content",
  "title": "À propos",
  "content": [
    {
      "_type": "block",
      "style": "normal",
      "children": [{ "_type": "span", "text": "Bienvenue sur notre site." }]
    }
  ]
}

Ajouter un widget de composant :

POST /_emdash/api/widget-areas/sidebar/widgets
Content-Type: application/json

{
  "type": "component",
  "title": "Articles récents",
  "componentId": "core:recent-posts",
  "componentProps": { "count": 5, "showDate": true }
}

Référence API

getWidgetArea(name)

Récupère une zone de widgets par nom avec tous ses widgets.

Paramètres :

  • name — Identifiant unique de la zone (chaîne)

Retour : Promise<WidgetArea | null>

getWidgetAreas()

Liste toutes les zones de widgets avec leurs widgets.

Retour : Promise<WidgetArea[]>

getWidgetComponents()

Liste les définitions de composants widget disponibles pour l’admin.

Retour : WidgetComponentDef[]