Á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 secção que editores devam controlar sem alterar código.

Consultar áreas de widgets

Use getWidgetArea() para obter uma área de widgets pelo 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 devolve null se a área de widgets não existir.

Estrutura da área de widgets

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

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

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

Tipos de widget

O EmDash suporta três tipos de widget:

Widgets de conteúdo

Texto rico armazenado como Portable Text. Renderize com 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

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

Renderizam um componente registado com props configuráveis. O EmDash inclui estes componentes principais:

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

Renderizar widgets

Crie um componente de renderização reutilizável:

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

Exemplos de componentes widget

Widget de artigos recentes

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

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

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

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

Áreas de widgets em layouts

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

Listar todas as áreas de widgets

Use getWidgetAreas() para obter todas as áreas com os respetivos widgets:

import { getWidgetAreas } from "emdash";

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

Criar áreas de widgets

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

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

{
  "name": "footer-1",
  "label": "Coluna do rodapé 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": "Artigos recentes",
  "componentId": "core:recent-posts",
  "componentProps": { "count": 5, "showDate": true }
}

Referência da API

getWidgetArea(name)

Obtém uma área de widgets pelo nome com todos os widgets.

Parâmetros:

  • name — Identificador único da área de widgets (string)

Devolve: Promise<WidgetArea | null>

getWidgetAreas()

Lista todas as áreas de widgets com os respetivos widgets.

Devolve: Promise<WidgetArea[]>

getWidgetComponents()

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

Devolve: WidgetComponentDef[]