위젯 영역
위젯 영역은 템플릿 내에서 관리자가 콘텐츠 블록을 배치할 수 있는 명명된 영역입니다. 사이드바, 푸터 열, 프로모션 배너 또는 편집자가 코드를 건드리지 않고 제어해야 하는 모든 섹션에 사용하세요.
위젯 영역 쿼리하기
섹션 제목: “위젯 영역 쿼리하기”getWidgetArea()를 사용하여 이름으로 위젯 영역을 가져옵니다:
---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 componentsimport 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-areasContent-Type: application/json
{ "name": "footer-1", "label": "Footer Column 1", "description": "First column in the footer"}콘텐츠 위젯 추가하기:
POST /_emdash/api/widget-areas/footer-1/widgetsContent-Type: application/json
{ "type": "content", "title": "소개", "content": [ { "_type": "block", "style": "normal", "children": [{ "_type": "span", "text": "우리 사이트에 오신 것을 환영합니다." }] } ]}컴포넌트 위젯 추가하기:
POST /_emdash/api/widget-areas/sidebar/widgetsContent-Type: application/json
{ "type": "component", "title": "최근 게시물", "componentId": "core:recent-posts", "componentProps": { "count": 5, "showDate": true }}API 참조
섹션 제목: “API 참조”getWidgetArea(name)
섹션 제목: “getWidgetArea(name)”이름으로 위젯 영역을 모든 위젯과 함께 가져옵니다.
매개변수:
name— 위젯 영역의 고유 식별자 (문자열)
반환값: Promise<WidgetArea | null>
getWidgetAreas()
섹션 제목: “getWidgetAreas()”모든 위젯 영역과 그 위젯들을 나열합니다.
반환값: Promise<WidgetArea[]>
getWidgetComponents()
섹션 제목: “getWidgetComponents()”관리자 UI를 위해 사용 가능한 위젯 컴포넌트 정의를 나열합니다.
반환값: WidgetComponentDef[]