このページで分かること

  • ウィジェットエリアの役割と、セクションとの使い分け
  • 管理画面でのウィジェットエリアの作成と、3種類のウィジェット(Content、Menu、Component)、組み込みのコンポーネント5つ
  • WidgetArea コンポーネントによるテンプレートへの配置、多言語サイトでのリンクの注意点、getWidgetArea() で独自に描画する方法
難易度
実践
読む時間
3分
前提知識
Astroとは
このページの目次

ウィジェットエリア(Widget area)は、サイトのテンプレート内にある、名前の付いた配置場所です。そこに何を表示するかは編集者が選び、エリアをどこに置き、出力をどう装飾するかはテンプレートが決めます。

サイドバー、フッターの列、告知メッセージなど、エリアを表示するすべての場所で内容をそろえておきたいコンテンツには、ウィジェットエリアを使います。編集者がエントリーの中に独立したコピーを挿入し、そのあとで内容を変えるような場合は、セクションを使います。

ウィジェットエリアの追加

ウィジェットエリアは、EmDashの管理画面の「ウィジェット」で作成し、中身を配置します。

  1. 「ウィジェットエリアを追加」をクリックします。テンプレートがクエリに使う名前、管理画面用のラベル、必要に応じて表示場所の説明を入力します。

  2. 「追加可能なウィジェット」から、新しいエリアにウィジェットをドラッグします。

  3. ウィジェットを設定し、エリア内のウィジェットをドラッグして順番を決めます。

利用できるウィジェットの種類は次のとおりです。

  • 「コンテンツ」は、編集者が入力したPortable Textを描画します。
  • 「メニュー」は、名前で選んだメニューを描画します。
  • 「コンポーネント」は、組み込みのコンポーネント(最近の投稿、カテゴリー、タグ、検索、アーカイブ)のうち1つを描画します。

表示件数の上限、日付、件数、検索欄のプレースホルダーのテキストなどの細かい設定は、管理画面のコンポーネントの設定で決めます。

使える組み込みのコンポーネントウィジェットは次のとおりです。

コンポーネント 描画する内容
core:recent-posts 最近の投稿。日付とサムネイルは任意
core:categories カテゴリーのリンク。エントリーの件数は任意
core:tags 件数を絞ったタグのリンクの一覧。件数は任意
core:search /search に送信する検索フォーム
core:archives 月別または年別の投稿アーカイブへのリンク
本サイトの補足 やさしい解説

WordPressの「外観」→「ウィジェット」にあたる機能です。WordPressではテーマが用意したサイドバーなどにウィジェットを置きましたが、EmDashでは管理画面でウィジェットエリアそのものを作り、テンプレートの好きな場所に名前で呼び出して置きます。置けるウィジェットは、文章を書く「コンテンツ」、既存のメニューを表示する「メニュー」、最近の投稿や検索フォームなどの「コンポーネント」の3種類です。

テンプレートへのエリアの配置

emdash/ui から WidgetArea をインポートします。このコンポーネントは名前で指定したエリアを取得し、設定された順番を保ったまま描画します。エリアが存在しないか空の場合は何も描画しません。

次のレイアウトは、ページのコンテンツの横にサイドバーのエリアを配置します。

src/layouts/BlogPost.astro
---
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 を付けます。各項目には widgetwidget__titlewidget__content のクラスが付きます。コンテンツ、メニュー、組み込みのコンポーネントの各ウィジェットは、その下にさらに具体的なクラスを付けます。

Astroコンポーネントのスタイルは、初期状態ではそのコンポーネントの中だけに適用されます。WidgetArea の中に描画されるマークアップを装飾するには、グローバルなstyleブロックを使います。

src/layouts/BlogPost.astro
<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>
本サイトの補足 やさしい解説

テンプレート側の作業は、<WidgetArea name="sidebar" /> のように、管理画面で付けた名前でエリアを呼び出すだけです。エリアに何を入れるか、どの順番に並べるかは、管理画面で編集者が変えられます。見た目を整えるCSSを書くときは、Astroの通常の <style> ではウィジェットの中身に届かないため、<style is:global> を使います。

メニュー、カテゴリー、タグの各ウィジェットは、現在のリクエストのロケールでデータをクエリします。メニューの参照も、翻訳されたコンテンツやタームがあれば、それに解決されます。

組み込みのウィジェットの描画処理は、メニューやタクソノミーのヘルパーが返す、ルートからの相対URLを使います。Astroのロケールの接頭辞は付けません。多言語サイトで、ナビゲーションやタクソノミーの一覧のリンクにロケールの接頭辞が必要な場合は、メニューの描画の方法ターム一覧の方法で描画します。コンテンツ、検索、最近の投稿、アーカイブの各ウィジェットは、引き続き標準の WidgetArea コンポーネントを使えます。

エリアを自分で描画する

組み込みの描画処理にないマークアップがサイトに必要な場合は、getWidgetArea() を呼び出します。次の例は、コンテンツとメニューのウィジェットを含むエリアを描画し、メニューのリンクにAstroのロケールの接頭辞を付けます。

まず、エリアを取得し、設定された各ウィジェットを描画用のコンポーネントに渡します。

src/components/LocalizedWidgetArea.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>
)}

次に、コンテンツとメニューのウィジェットを明示的に処理します。

src/components/LocalizedWidget.astro
---
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ごとの処理を明示的に追加します。

getWidgetArea()getWidgetAreas()ランタイムAPIリファレンスで説明しています。プログラムから変更する場合は、Bearerトークンで認証し、状態を変更するすべてのリクエストに X-EmDash-Request: 1 を付けます。リクエストの本文とレスポンスはウィジェットエリアのエンドポイントを参照してください。