Áreas de widgets

En esta página

Las áreas de widgets son regiones nombradas en tus plantillas donde los administradores pueden colocar bloques de contenido. Úsalas para barras laterales, columnas de pie de página, 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 widget 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 widget no existe.

Estructura del área de widgets

Un área de widget 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 específicos del tipo
	content?: PortableTextBlock[]; // Para widgets de contenido
	menuName?: string; // Para widgets de menú
	componentId?: string; // Para widgets de componente
	componentProps?: Record<string, unknown>;
}

Tipos de widgets

EmDash soporta tres tipos de widgets:

Widgets de contenido

Contenido de texto enriquecido almacenado como Portable Text. Renderízalo usando 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 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 componente

Renderiza un componente registrado con props configurables. EmDash incluye estos componentes principales:

ID de componenteDescripciónProps
core:recent-postsLista de posts recientescount, showThumbnails, showDate
core:categoriesLista de categoríasshowCount, hierarchical
core:tagsNube de tagsshowCount, limit
core:searchFormulario de búsquedaplaceholder
core:archivesArchivos mensuales/anualestype, limit

Renderizar widgets

Crea un componente renderizador de widgets reutilizable:

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

// Importar tus componentes 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>

Ejemplo de componentes widget

Widget de posts recientes

El siguiente componente renderiza los posts más recientes, con miniaturas y fechas opcionales:

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

El siguiente componente renderiza un formulario de búsqueda que envía a una página 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>

Usar áreas de widgets en layouts

El siguiente ejemplo muestra un layout de blog con un área de widget en la 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 de widget con sus widgets:

import { getWidgetAreas } from "emdash";

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

Crear áreas de widgets

Crea áreas de widgets a través de la interfaz de administración en /_emdash/admin/widgets, o usa la API de administración:

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

{
  "name": "footer-1",
  "label": "Pie de página columna 1",
  "description": "Primera columna en el 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": "Posts recientes",
  "componentId": "core:recent-posts",
  "componentProps": { "count": 5, "showDate": true }
}

Referencia de API

getWidgetArea(name)

Obtener un área de widget por nombre con todos los widgets.

Parámetros:

  • name — El identificador único del área de widget (string)

Devuelve: Promise<WidgetArea | null>

getWidgetAreas()

Listar todas las áreas de widget con sus widgets.

Devuelve: Promise<WidgetArea[]>

getWidgetComponents()

Listar las definiciones de componentes widget disponibles para la UI de administración.

Devuelve: WidgetComponentDef[]