Áreas de widgets

En esta página

Las áreas de widgets son regiones con nombre en tus plantillas donde los administradores pueden colocar bloques de contenido. Úsalas para barras laterales, columnas del pie, banners promocionales o cualquier sección que los editores deban controlar sin tocar código.

Consultar áreas de widgets

Usa getWidgetArea() para obtener un área de widgets por nombre:

---
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>}
        <!-- Renderizar contenido del widget -->
      </div>
    ))}
  </aside>
)}

La función devuelve null si el área de widgets no existe.

Estructura del área de widgets

Un área de widgets contiene metadatos y un array de widgets:

interface WidgetArea {
	id: string;
	name: string; // Identificador único ("sidebar", "footer-1")
	label: string; // Nombre visible ("Barra lateral principal")
	description?: string;
	widgets: Widget[];
}

interface Widget {
	id: string;
	type: "content" | "menu" | "component";
	title?: string;
	// Campos según el tipo
	content?: PortableTextBlock[]; // Widgets de contenido
	menuName?: string; // Widgets de menú
	componentId?: string; // Widgets de componente
	componentProps?: Record<string, unknown>;
}

Tipos de widgets

EmDash admite tres tipos de widgets:

Widgets de contenido

Texto enriquecido almacenado como Portable Text. Renderízalo con el componente PortableText:

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

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

Widgets de menú

Muestra un menú de navegación dentro de un área 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 componente

Renderiza un componente registrado con props configurables. EmDash incluye estos componentes básicos:

ID de componenteDescripciónProps
core:recent-postsLista de entradas recientescount, showThumbnails, showDate
core:categoriesLista de categoríasshowCount, hierarchical
core:tagsNube de etiquetasshowCount, limit
core:searchFormulario de búsquedaplaceholder
core:archivesArchivos por mes/añotype, limit

Renderizar widgets

Crea un componente reutilizable para renderizar widgets:

---
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>

Ejemplos de componentes de widget

Widget de entradas recientes

---
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 búsqueda

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

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

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

Áreas de widgets en los layouts

El siguiente ejemplo muestra un layout de blog con barra lateral:

---
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>

Listar todas las áreas de widgets

Usa getWidgetAreas() para obtener todas las áreas con sus widgets:

import { getWidgetAreas } from "emdash";

const areas = await getWidgetAreas();
// Devuelve todas las áreas con widgets cargados

Crear áreas de widgets

Créalas desde el admin en /_emdash/admin/widgets o con la API de administración:

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

{
  "name": "footer-1",
  "label": "Pie columna 1",
  "description": "Primera columna del pie de página"
}

Añadir un widget de contenido:

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

{
  "type": "content",
  "title": "Sobre nosotros",
  "content": [
    {
      "_type": "block",
      "style": "normal",
      "children": [{ "_type": "span", "text": "Bienvenido a nuestro sitio." }]
    }
  ]
}

Añadir un widget de componente:

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

{
  "type": "component",
  "title": "Entradas recientes",
  "componentId": "core:recent-posts",
  "componentProps": { "count": 5, "showDate": true }
}

Referencia de la API

getWidgetArea(name)

Obtiene un área de widgets por nombre con todos sus widgets.

Parámetros:

  • name — Identificador único del área (cadena)

Devuelve: Promise<WidgetArea | null>

getWidgetAreas()

Lista todas las áreas de widgets con sus widgets.

Devuelve: Promise<WidgetArea[]>

getWidgetComponents()

Lista las definiciones de componentes de widget disponibles para el admin.

Devuelve: WidgetComponentDef[]