콘텐츠로 이동

위젯 영역

위젯 영역은 템플릿 내에서 관리자가 콘텐츠 블록을 배치할 수 있는 명명된 영역입니다. 사이드바, 푸터 열, 프로모션 배너 또는 편집자가 코드를 건드리지 않고 제어해야 하는 모든 섹션에 사용하세요.

getWidgetArea()를 사용하여 이름으로 위젯 영역을 가져옵니다:

src/layouts/Base.astro
---
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>
}
<!-- Render widget content -->
</div>
))}
</aside>
)}

함수는 위젯 영역이 존재하지 않으면 null을 반환합니다.

위젯 영역은 메타데이터와 위젯 배열을 포함합니다:

interface WidgetArea {
id: string;
name: string; // 고유 식별자 ("sidebar", "footer-1")
label: string; // 표시 이름 ("메인 사이드바")
description?: string;
widgets: Widget[];
}
interface Widget {
id: string;
type: "content" | "menu" | "component";
title?: string;
// Type-specific fields
content?: PortableTextBlock[]; // For content widgets
menuName?: string; // For menu widgets
componentId?: string; // For component widgets
componentProps?: Record<string, unknown>;
}

EmDash는 세 가지 위젯 유형을 지원합니다:

Portable Text로 저장된 리치 텍스트 콘텐츠입니다. PortableText 컴포넌트를 사용하여 렌더링하세요:

---
import { PortableText } from "emdash/ui";
---
{widget.type === "content" && widget.content && (
<div class="widget-content">
<PortableText value={widget.content} />
</div>
)}

위젯 영역 내에 네비게이션 메뉴를 표시합니다:

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

구성 가능한 props로 등록된 컴포넌트를 렌더링합니다. EmDash에는 다음과 같은 핵심 컴포넌트가 포함됩니다:

컴포넌트 ID설명Props
core:recent-posts최근 게시물 목록count, showThumbnails, showDate
core:categories카테고리 목록showCount, hierarchical
core:tags태그 클라우드showCount, limit
core:search검색 폼placeholder
core:archives월별/연도별 아카이브type, limit

재사용 가능한 위젯 렌더러 컴포넌트를 만듭니다:

astro title="src/components/WidgetRenderer.astro"
---
import { PortableText } from "emdash/ui";
import { getMenu } from "emdash";
import type { Widget } from "emdash";
// Import your widget components
import RecentPosts from "../../guides/widgets/RecentPosts.astro";
import Categories from "../../guides/widgets/Categories.astro";
import TagCloud from "../../guides/widgets/TagCloud.astro";
import SearchForm from "../../guides/widgets/SearchForm.astro";
import Archives from "../../guides/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>
astro title="src/components/widgets/RecentPosts.astro"
---
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>
astro title="src/components/widgets/SearchForm.astro"
---
interface Props {
placeholder?: string;
}
const { placeholder = "Search..." } = Astro.props;
---
<form action="/search" method="get" class="search-form">
<input
type="search"
name="q"
placeholder={placeholder}
aria-label="Search"
/>
<button type="submit">Search</button>
</form>

레이아웃에서 위젯 영역 사용하기

섹션 제목: “레이아웃에서 위젯 영역 사용하기”

다음 예제는 사이드바 위젯 영역이 있는 블로그 레이아웃을 보여줍니다:

astro title="src/layouts/BlogPost.astro"
---
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>

getWidgetAreas()를 사용하여 모든 위젯 영역과 그 위젯들을 검색합니다:

import { getWidgetAreas } from "emdash";
const areas = await getWidgetAreas();
// 위젯이 채워진 모든 영역을 반환합니다

/_emdash/admin/widgets의 관리자 인터페이스를 통해 위젯 영역을 생성하거나, 관리자 API를 사용하세요:

POST /_emdash/api/widget-areas
Content-Type: application/json
{
"name": "footer-1",
"label": "Footer Column 1",
"description": "First column in the footer"
}

콘텐츠 위젯 추가하기:

POST /_emdash/api/widget-areas/footer-1/widgets
Content-Type: application/json
{
"type": "content",
"title": "소개",
"content": [
{
"_type": "block",
"style": "normal",
"children": [{ "_type": "span", "text": "우리 사이트에 오신 것을 환영합니다." }]
}
]
}

컴포넌트 위젯 추가하기:

POST /_emdash/api/widget-areas/sidebar/widgets
Content-Type: application/json
{
"type": "component",
"title": "최근 게시물",
"componentId": "core:recent-posts",
"componentProps": { "count": 5, "showDate": true }
}

이름으로 위젯 영역을 모든 위젯과 함께 가져옵니다.

매개변수:

  • name — 위젯 영역의 고유 식별자 (문자열)

반환값: Promise<WidgetArea | null>

모든 위젯 영역과 그 위젯들을 나열합니다.

반환값: Promise<WidgetArea[]>

관리자 UI를 위해 사용 가능한 위젯 컴포넌트 정의를 나열합니다.

반환값: WidgetComponentDef[]