Zones de widgets

Sur cette page

Les zones de widgets sont des régions nommées dans vos templates où les administrateurs peuvent placer des blocs de contenu. Utilisez-les 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 widget par 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>}
        <!-- Rendre le contenu du widget -->
      </div>
    ))}
  </aside>
)}

La fonction retourne null si la zone de widget n’existe pas.

Structure d’une zone de widgets

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

interface WidgetArea {
	id: string;
	name: string; // Identifiant unique ("sidebar", "footer-1")
	label: string; // Nom d'affichage ("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[]; // Pour les widgets de contenu
	menuName?: string; // Pour les widgets de menu
	componentId?: string; // Pour les widgets de composant
	componentProps?: Record<string, unknown>;
}

Types de widgets

EmDash prend en charge trois types de widgets :

Widgets de contenu

Contenu texte riche 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 widget :

---
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 du 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 de widgets réutilisable :

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

// Importer vos composants de widget
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 des articles récents

Le composant suivant rend les articles les plus récents, avec des miniatures et dates optionnelles :

---
import { getEmDashCollection } from "emdash";
import { Image } from "emdash/ui";

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 && (
        <Image image={post.data.featured_image} alt="" class="thumbnail" />
      )}
      <a href={`/posts/${post.data.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

Le composant suivant rend un formulaire de recherche qui soumet vers une page 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="Rechercher"
  />
  <button type="submit">Rechercher</button>
</form>

Utiliser les zones de widgets dans les layouts

L’exemple suivant montre un layout de blog avec une zone de widget dans la 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 de widget avec leurs widgets :

import { getWidgetAreas } from "emdash";

const areas = await getWidgetAreas();
// Retourne toutes les zones avec les widgets remplis

Créer des zones de widgets

Créez des zones de widgets via l’interface d’administration à /_emdash/admin/widgets, ou utilisez 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érer une zone de widget par nom avec tous les widgets.

Paramètres :

  • name — L’identifiant unique de la zone de widget (string)

Retourne : Promise<WidgetArea | null>

getWidgetAreas()

Lister toutes les zones de widget avec leurs widgets.

Retourne : Promise<WidgetArea[]>

getWidgetComponents()

Lister les définitions de composants widget disponibles pour l’UI d’administration.

Retourne : WidgetComponentDef[]