ウィジェットエリア

このページ

ウィジェットエリアは、管理者がコンテンツブロックを配置できるテンプレート内の名前付き領域です。サイドバー、フッターカラム、プロモーションバナー、またはエディターがコードに触れずに制御すべきあらゆるセクションに使用します。

ウィジェットエリアのクエリ

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>}
        <!-- ウィジェットコンテンツをレンダリング -->
      </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;
	// タイプ固有のフィールド
	content?: PortableTextBlock[]; // コンテンツウィジェット用
	menuName?: string; // メニューウィジェット用
	componentId?: string; // コンポーネントウィジェット用
	componentProps?: Record<string, unknown>;
}

ウィジェットタイプ

EmDashは3つのウィジェットタイプをサポートしています:

コンテンツウィジェット

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

ウィジェットのレンダリング

再利用可能なウィジェットレンダラーコンポーネントを作成します:

---
import { PortableText } from "emdash/ui";
import { getMenu } from "emdash";
import type { Widget } from "emdash";

// ウィジェットコンポーネントをインポート
import RecentPosts from "./widgets/RecentPosts.astro";
import Categories from "./widgets/Categories.astro";
import TagCloud from "./widgets/TagCloud.astro";
import SearchForm from "./widgets/SearchForm.astro";
import Archives from "./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>

ウィジェットコンポーネントの例

最新投稿ウィジェット

以下のコンポーネントは、オプションのサムネイルと日付を含む最新の投稿をレンダリングします:

---
import { getEmDashCollection } from "emdash";
import { Image } from "emdash/ui";

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 && (
        <Image image={post.data.featured_image} alt="" class="thumbnail" />
      )}
      <a href={`/posts/${post.data.slug}`}>{post.data.title}</a>
      {showDate && post.data.publishedAt && (
        <time datetime={post.data.publishedAt.toISOString()}>
          {post.data.publishedAt.toLocaleDateString()}
        </time>
      )}
    </li>
  ))}
</ul>

検索ウィジェット

以下のコンポーネントは検索ページに送信する検索フォームをレンダリングします:

---
interface Props {
  placeholder?: string;
}

const { placeholder = "検索..." } = Astro.props;
---

<form action="/search" method="get" class="search-form">
  <input
    type="search"
    name="q"
    placeholder={placeholder}
    aria-label="検索"
  />
  <button type="submit">検索</button>
</form>

レイアウトでウィジェットエリアを使用する

以下の例は、サイドバーウィジェットエリアを持つブログレイアウトを示しています:

---
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": "フッターカラム 1",
  "description": "フッターの最初のカラム"
}

コンテンツウィジェットの追加:

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

APIリファレンス

getWidgetArea(name)

名前でウィジェットエリアとそのすべてのウィジェットを取得します。

パラメータ:

  • name — ウィジェットエリアの一意識別子(string)

戻り値: Promise<WidgetArea | null>

getWidgetAreas()

すべてのウィジェットエリアとそのウィジェットを一覧表示します。

戻り値: Promise<WidgetArea[]>

getWidgetComponents()

管理UI用の利用可能なウィジェットコンポーネント定義を一覧表示します。

戻り値: WidgetComponentDef[]