Áreas de widgets

Nesta página

Áreas de widgets são regiões nomeadas nos seus templates onde administradores podem colocar blocos de conteúdo. Use-as para barras laterais, colunas de rodapé, banners promocionais ou qualquer seção que editores devem controlar sem tocar no código.

Consultando áreas de widgets

Use getWidgetArea() para buscar uma área de widget por nome:

---
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 conteúdo do widget -->
      </div>
    ))}
  </aside>
)}

A função retorna null se a área de widget não existir.

Estrutura da área de widgets

Uma área de widget contém metadados e um array de widgets:

interface WidgetArea {
	id: string;
	name: string; // Identificador único ("sidebar", "footer-1")
	label: string; // Nome de exibição ("Barra Lateral Principal")
	description?: string;
	widgets: Widget[];
}

interface Widget {
	id: string;
	type: "content" | "menu" | "component";
	title?: string;
	// Campos específicos do tipo
	content?: PortableTextBlock[]; // Para widgets de conteúdo
	menuName?: string; // Para widgets de menu
	componentId?: string; // Para widgets de componente
	componentProps?: Record<string, unknown>;
}

Tipos de widgets

O EmDash suporta três tipos de widgets:

Widgets de conteúdo

Conteúdo de texto rico armazenado como Portable Text. Renderize usando o componente PortableText:

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

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

Widgets de menu

Exibir um menu de navegação dentro de uma á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

Renderize um componente registrado com props configuráveis. O EmDash inclui estes componentes principais:

ID do componenteDescriçãoProps
core:recent-postsLista de posts recentescount, showThumbnails, showDate
core:categoriesLista de categoriasshowCount, hierarchical
core:tagsNuvem de tagsshowCount, limit
core:searchFormulário de buscaplaceholder
core:archivesArquivos mensais/anuaistype, limit

Renderizando widgets

Crie um componente renderizador de widgets reutilizável:

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

// Importar seus 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>

Exemplos de componentes widget

Widget de posts recentes

O seguinte componente renderiza os posts mais recentes, com thumbnails e datas opcionais:

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

O seguinte componente renderiza um formulário de busca que envia para uma página de busca:

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

Usando áreas de widgets em layouts

O seguinte exemplo mostra um layout de blog com uma área de widget na 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>

Listando todas as áreas de widgets

Use getWidgetAreas() para recuperar todas as áreas de widget com seus widgets:

import { getWidgetAreas } from "emdash";

const areas = await getWidgetAreas();
// Retorna todas as áreas com widgets preenchidos

Criando áreas de widgets

Crie áreas de widgets através da interface admin em /_emdash/admin/widgets, ou use a API de administração:

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

{
  "name": "footer-1",
  "label": "Rodapé Coluna 1",
  "description": "Primeira coluna no rodapé"
}

Adicionar um widget de conteúdo:

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

{
  "type": "content",
  "title": "Sobre nós",
  "content": [
    {
      "_type": "block",
      "style": "normal",
      "children": [{ "_type": "span", "text": "Bem-vindo ao nosso site." }]
    }
  ]
}

Adicionar um widget de componente:

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

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

Referência da API

getWidgetArea(name)

Buscar uma área de widget por nome com todos os widgets.

Parâmetros:

  • name — O identificador único da área de widget (string)

Retorna: Promise<WidgetArea | null>

getWidgetAreas()

Listar todas as áreas de widget com seus widgets.

Retorna: Promise<WidgetArea[]>

getWidgetComponents()

Listar definições de componentes widget disponíveis para a UI de administração.

Retorna: WidgetComponentDef[]