小工具區域

本頁內容

小工具區域是站台範本中的一個命名位置。編輯決定那裡顯示什麼,而範本控制區域放在哪裡以及其輸出如何設定樣式。

對於需要在區域出現的任何位置保持同步的內容,請使用小工具區域,例如側邊欄、頁尾欄或推廣訊息。如果編輯需要在項目中插入一份獨立的副本並隨後自訂它,請改用 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。請求主體和回應請參閱小工具區域端點。