ウィジェットエリア

このページ

ウィジェットエリアは、サイトテンプレート内の名前付きの位置です。そこに何を表示するかはエディターが選び、エリアをどこに置くか、出力をどうスタイルするかはテンプレートが制御します。

ウィジェットエリアは、エリアが表示されるどの場所でも同期された状態を保つべきコンテンツに使います。たとえば、サイドバー、フッターの列、プロモーションメッセージなどです。エディターがエントリー内に独立したコピーを挿入し、そのあとカスタマイズする場合は、代わりにセクションを使います。

ウィジェットエリアを追加する

ウィジェットエリアは、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(スラッグのみ)、:path(スラッグまたは 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 を追加します。リクエストボディとレスポンスについては、ウィジェットエリアのエンドポイントを参照してください。