小工具区域是站点模板中的一个命名位置。编辑决定那里显示什么,而模板控制区域放在哪里以及其输出如何设置样式。
对于需要在区域出现的任何位置保持同步的内容,请使用小工具区域,例如侧边栏、页脚栏或推广消息。如果编辑需要在条目中插入一份独立的副本并随后自定义它,请改用 section。
添加小工具区域
在 EmDash 管理后台的 Widgets 中创建并填充小工具区域。
-
点击 Add Widget Area。输入模板将要查询的名称、管理后台中显示的标签,以及可选的出现位置说明。
-
将小部件从 Available Widgets 拖入新区域。
-
配置该小部件,然后在区域内拖动小部件以设置其顺序。
可用的小部件类型如下:
- 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。请求体和响应请参阅小工具区域端点。