EmDash 選單是您透過管理介面管理的有序連結清單。選單支援下拉式選單的巢狀結構,可以連結到頁面、文章、分類術語或外部 URL。
查詢選單
使用 getMenu() 透過唯一名稱取得選單:
---
import { getMenu } from "emdash";
const primaryMenu = await getMenu("primary");
---
{primaryMenu && (
<nav>
<ul>
{primaryMenu.items.map(item => (
<li>
<a href={item.url}>{item.label}</a>
</li>
))}
</ul>
</nav>
)}
如果不存在該名稱的選單,函式將返回 null。
選單結構
選單包含中繼資料和項目陣列:
interface Menu {
id: string;
name: string; // 唯一識別碼("primary"、"footer")
label: string; // 顯示名稱("Primary Navigation")
items: MenuItem[];
}
interface MenuItem {
id: string;
label: string;
url: string; // 解析後的 URL
target?: string; // 新視窗使用 "_blank"
titleAttr?: string; // HTML title 屬性
cssClasses?: string; // 自訂 CSS 類別
children: MenuItem[]; // 用於下拉式選單的巢狀項目
}
URL 根據項目類型自動解析:
- 頁面/文章項目解析為
/{collection}/{slug} - 分類項目解析為
/{taxonomy}/{slug} - 集合項目解析為
/{collection}/ - 自訂連結按原樣使用 URL
渲染巢狀選單
選單項目可以具有用於下拉式導航的子項目。透過遞迴渲染 children 陣列來處理巢狀:
---
import { getMenu } from "emdash";
import type { MenuItem } from "emdash";
interface Props {
name: string;
}
const menu = await getMenu(Astro.props.name);
---
{menu && (
<nav class="nav">
<ul class="nav-list">
{menu.items.map(item => (
<li class:list={["nav-item", item.cssClasses]}>
<a
href={item.url}
target={item.target}
title={item.titleAttr}
aria-current={Astro.url.pathname === item.url ? "page" : undefined}
>
{item.label}
</a>
{item.children.length > 0 && (
<ul class="submenu">
{item.children.map(child => (
<li>
<a href={child.url} target={child.target}>
{child.label}
</a>
</li>
))}
</ul>
)}
</li>
))}
</ul>
</nav>
)}
選單項目類型
管理面板支援五種類型的選單項目:
| 類型 | 描述 | URL 解析 |
|---|---|---|
page | 連結到頁面 | /{collection}/{slug} |
post | 連結到文章 | /{collection}/{slug} |
taxonomy | 連結到分類或標籤 | /{taxonomy}/{slug} |
collection | 連結到集合封存 | /{collection}/ |
custom | 外部或自訂 URL | 按原樣使用 |
列出所有選單
使用 getMenus() 檢索所有選單定義(不含項目):
import { getMenus } from "emdash";
const menus = await getMenus();
// 返回:[{ id, name, label, locale }, ...]
這主要用於管理介面或除錯。
建立選單
透過 /_emdash/admin/menus 的管理介面建立選單,或使用管理 API:
POST /_emdash/api/menus
Content-Type: application/json
{
"name": "footer",
"label": "Footer Navigation"
}
向選單新增項目:
POST /_emdash/api/menus/footer/items
Content-Type: application/json
{
"type": "page",
"referenceCollection": "pages",
"referenceId": "page_privacy",
"label": "Privacy Policy"
}
新增自訂外部連結:
POST /_emdash/api/menus/footer/items
Content-Type: application/json
{
"type": "custom",
"customUrl": "https://github.com/example",
"label": "GitHub",
"target": "_blank"
}
重新排序和巢狀
使用重新排序端點更新項目順序和父子關係:
POST /_emdash/api/menus/primary/reorder
Content-Type: application/json
{
"items": [
{ "id": "item_1", "parentId": null, "sortOrder": 0 },
{ "id": "item_2", "parentId": null, "sortOrder": 1 },
{ "id": "item_3", "parentId": "item_2", "sortOrder": 0 }
]
}
這使 item_3 成為 item_2 的子項目,建立下拉式選單。
完整範例
以下範例顯示了帶有主導航的響應式頁眉:
---
import { getMenu, getSiteSettings } from "emdash";
const settings = await getSiteSettings();
const primaryMenu = await getMenu("primary");
---
<html lang="en">
<head>
<title>{settings.title}</title>
</head>
<body>
<header class="header">
<a href="/" class="logo">
{settings.logo ? (
<img src={settings.logo.url} alt={settings.logo.alt || settings.title} />
) : (
settings.title
)}
</a>
{primaryMenu && (
<nav class="main-nav" aria-label="Main navigation">
<ul>
{primaryMenu.items.map(item => (
<li class:list={[item.cssClasses, { "has-children": item.children.length > 0 }]}>
<a
href={item.url}
target={item.target}
aria-current={Astro.url.pathname === item.url ? "page" : undefined}
>
{item.label}
</a>
{item.children.length > 0 && (
<ul class="dropdown">
{item.children.map(child => (
<li>
<a href={child.url} target={child.target}>{child.label}</a>
</li>
))}
</ul>
)}
</li>
))}
</ul>
</nav>
)}
</header>
<main>
<slot />
</main>
</body>
</html>
API 參考
getMenu(name)
透過名稱取得包含所有項目和已解析 URL 的選單。
參數:
name— 選單的唯一識別碼(字串)
返回: Promise<Menu | null>
getMenus()
列出所有選單定義(不含項目)。
返回: Promise<Array<{ id: string; name: string; label: string; locale: string }>>