小工具区域

本页内容

小工具区域是站点模板中的一个命名位置。编辑决定那里显示什么,而模板控制区域放在哪里以及其输出如何设置样式。

对于需要在区域出现的任何位置保持同步的内容,请使用小工具区域,例如侧边栏、页脚栏或推广消息。如果编辑需要在条目中插入一份独立的副本并随后自定义它,请改用 section。

添加小工具区域

在 EmDash 管理后台的 Widgets 中创建并填充小工具区域。

  1. 点击 Add Widget Area。输入模板将要查询的名称、管理后台中显示的标签,以及可选的出现位置说明。

  2. 将小部件从 Available Widgets 拖入新区域。

  3. 配置该小部件,然后在区域内拖动小部件以设置其顺序。

可用的小部件类型如下:

  • Content 渲染编辑输入的 Portable Text。
  • Menu 渲染按名称选择的菜单。
  • Component 渲染内置组件之一:最新文章、分类、标签、搜索或归档。

管理后台中的组件设置可控制条目数量上限、日期、计数和搜索占位文本等细节。

以下内置组件小部件可用:

组件渲染内容
core:recent-posts最新文章,可选显示日期、缩略图和链接 URL 模板(例如 /blog/:slug)
core:categories分类链接和可选的条目计数
core:tags数量有限的标签链接列表和可选的计数
core:search提交到 /search 的搜索表单
core:archives按月或按年的文章归档链接

core:recent-posts 的链接模板接受与 LiveSearch 的 routeMap 相同的标记::collection、:id(内容的 ULID)、:slug(纯 slug)和 :path(slug 或 ID)。没有模板时,链接保持其现有形式,包括任何 Astro i18n 语言前缀。

在模板中放置区域

从 emdash/ui 导入 WidgetArea。该组件会获取指定名称的区域、保持配置的顺序,并在区域不存在或为空时不渲染任何内容。

以下布局将侧边栏区域放在页面内容旁边:

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

<div class="page-with-sidebar">
  <main>
    <slot />
  </main>

  <aside aria-label="Sidebar">
    <WidgetArea name="sidebar" class="sidebar-widgets" />
  </aside>
</div>

WidgetArea 会在其包装元素上添加 widget-area 以及传入的 class。每个条目使用 widget、widget__title 和 widget__content 类。内容、菜单和内置组件小部件会在其下添加更具体的类。

Astro 组件样式默认是作用域限定的。为 WidgetArea 内部渲染的标记设置样式时,请使用全局样式块:

<style is:global>
  .page-with-sidebar {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 18rem;
    gap: 2rem;
  }

  .sidebar-widgets {
    display: grid;
    gap: 1.5rem;
  }

  .sidebar-widgets .widget__title {
    margin-block-end: 0.75rem;
  }

  @media (max-width: 48rem) {
    .page-with-sidebar {
      grid-template-columns: 1fr;
    }
  }
</style>

小部件中的菜单与分类法

菜单、分类和标签小部件会按当前请求的语言查询数据。当存在翻译后的内容或术语时,菜单引用也会解析到对应的翻译版本。

内置小部件渲染器使用菜单或分类法辅助函数返回的根相对 URL,不会添加 Astro 的语言前缀。在多语言站点上,如果这些链接需要语言前缀,请使用菜单渲染模式或术语列表模式来渲染导航和分类法列表。内容、搜索、最新文章和归档小部件仍可使用标准的 WidgetArea 组件。

自行渲染区域

当站点需要内置渲染器无法提供的标记时,请调用 getWidgetArea()。以下示例渲染一个包含内容小部件和菜单小部件的区域,并为菜单链接添加 Astro 的语言前缀。

首先,获取区域并将每个已配置的小部件传给渲染器:

---
import { getWidgetArea } from "emdash";
import LocalizedWidget from "./LocalizedWidget.astro";

interface Props {
  name: string;
}

const area = await getWidgetArea(Astro.props.name);
---

{area && area.widgets.length > 0 && (
  <div class="widget-area" data-widget-area={area.name}>
    {area.widgets.map((widget) => (
      <LocalizedWidget widget={widget} />
    ))}
  </div>
)}

然后显式处理内容小部件和菜单小部件:

---
import { getMenu } from "emdash";
import type { Widget } from "emdash";
import { PortableText } from "emdash/ui";
import { getRelativeLocaleUrl } from "astro:i18n";

interface Props {
  widget: Widget;
}

const { widget } = Astro.props;
const locale = Astro.currentLocale;
const menu = widget.type === "menu" && widget.menuName
  ? await getMenu(widget.menuName, { locale })
  : null;

function menuHref(url: string) {
  return locale && url.startsWith("/")
    ? getRelativeLocaleUrl(locale, url)
    : url;
}
---

{widget.type !== "component" && (
  <section class="widget" data-widget-id={widget.id}>
    {widget.title && <h3>{widget.title}</h3>}

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

    {widget.type === "menu" && menu && (
      <nav aria-label={widget.title}>
        <ul>
          {menu.items.map((item) => (
            <li>
              <a
                href={menuHref(item.url)}
                target={item.target}
                rel={item.target === "_blank" ? "noopener noreferrer" : undefined}
              >
                {item.label}
              </a>
            </li>
          ))}
        </ul>
      </nav>
    )}
  </section>
)}

这个专用渲染器不会为组件小部件生成任何输出。请将组件小部件保留在标准的 WidgetArea 中,或为你要支持的站点组件添加显式的组件 ID 分支。

运行时 API 参考记录了 getWidgetArea() 和 getWidgetAreas()。如需以编程方式修改,请使用 Bearer 令牌进行身份验证,并在每个会改变状态的请求中添加 X-EmDash-Request: 1。请求体和响应请参阅小工具区域端点。