Á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 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; // Unique identifier ("sidebar", "footer-1")
	label: string; // Display name ("Main Sidebar")
	description?: string;
	widgets: Widget[];
}

interface Widget {
	id: string;
	type: "content" | "menu" | "component";
	title?: string;
	// Type-specific fields
	content?: PortableTextBlock[]; // For content widgets
	menuName?: string; // For menu widgets
	componentId?: string; // For component widgets
	componentProps?: Record<string, unknown>;
}

Tipos de Widgets

EmDash admite tres tipos de widgets:

Widgets de Contenido

Contenido de texto enriquecido almacenado como Portable Text. Renderiza 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 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 Componentes

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

Component IDDescripciónProps
core:recent-postsLista de publicaciones recientescount, showThumbnails, showDate
core:categoriesLista de categoríasshowCount, hierarchical
core:tagsNube de etiquetasshowCount, limit
core:searchFormulario de búsquedaplaceholder
core:archivesArchivos mensuales/anualestype, limit

Renderizar Widgets

Crea un componente de renderizado de widgets reutilizable:

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

// Import your widget components
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 Widgets

Widget de Publicaciones Recientes

El siguiente componente renderiza las publicaciones más recientes, con miniaturas y fechas opcionales:

---
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.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 = "Search..." } = Astro.props;
---

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

Usar Áreas de Widgets en Layouts

El siguiente ejemplo muestra un layout de blog con un área de widgets 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 recuperar todas las áreas de widgets con sus widgets:

import { getWidgetAreas } from "emdash";

const areas = await getWidgetAreas();
// Returns all areas with widgets populated

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": "Footer Column 1",
  "description": "First column in the footer"
}

Añadir un widget de contenido:

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

{
  "type": "content",
  "title": "About Us",
  "content": [
    {
      "_type": "block",
      "style": "normal",
      "children": [{ "_type": "span", "text": "Welcome to our site." }]
    }
  ]
}

Añadir un widget de componente:

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

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

Referencia de API

getWidgetArea(name)

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

Parámetros:

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

Devuelve: Promise<WidgetArea | null>

getWidgetAreas()

Lista todas las áreas de widgets con sus widgets.

Devuelve: Promise<WidgetArea[]>

getWidgetComponents()

Lista las definiciones de componentes de widgets disponibles para la interfaz de administración.

Devuelve: WidgetComponentDef[]