6
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Elixir Phoenix でパンくず(breadcrumb)コンポーネントを作る

Last updated at Posted at 2023-08-24

ウエブアプリのパンくずリストが好きなので、Elixir Phoenixアプリでも使えるようにコンポーネントを作ってみました。

目標

あまり複雑にしたくないので、あまりスマートなことはやらずに明示的にリストを渡す方針にしました。というかシンプルなコードしか思いつきません。

以下のようなインターフェイスを目指します。

<.breadcrumb items={[
  %{text: "Home", navigate: ~p"/"},
  %{text: "Examples", navigate: ~p"/examples"},
  %{text: "Light"}
]} />

liveview-breadcrumb 2023-08-11 at 22.40.14.gif

画像のサンプルアプリはPragmatic Studio Phoenix LiveView Courseについてきたものです。それにパンくずリストをつけて遊びました。

HTMLとTailwind

Phoenix 1.7にはデフォルトでTailwind CSSが付属しています。 特に設定を変更しなくてもTailwind CSSが機能するようになっています。

まず最初に、Tailwind CSSを使用したHTMLの例をインターネットで検索しました。私の焦点は、HTMLTailwind CSSではなく、ElixirPhoenixを使用してプログラミングを楽しむことですので、あえてTailwind CSSの深掘りはしません。

このサイトの例をベースにすることにしました。

Phoenix.Component.link/1

リンクについては、Phoenix.Component.link/1コンポーネントとその:naviigate属性を利用して、LiveViewページ間をスムーズに移動できるようにしました。

アイコン

アイコンはパンくずリストとは直接関係ないのですが、場合により使用したくなるかもしれません。Phoenix1.7にはheroiconsSVGが同梱されており、これらのSVGアイコンはMyAppWeb.CoreComponents.iconコンポーネントとして簡単に利用できます。

MyAppWeb.CoreComponentsモジュール内のすべてのコンポーネントはPhoenix 1.7の初期設定でインポートされていますので、コンポーネントのモジュール名は省略可能です。

.iconコンポーネントのおかげでheroiconsライブラリが提供するものに満足している限り、アイコンの設定について頭を悩ませる必要はなくなりました。

パンくずリストの関数コンポーネント

パンくずリスト関数コンポーネント専用の MyAppWeb.Breadcrumbという名前の新しいモジュールを作成しました。

MyAppWeb.CoreComponentsモジュールに追加する手もありますが、ここではパンくずリストの問題に集中できるように、新しいモジュールを作成することにしました。

これが私が作成したモジュールの全体像です。

defmodule MyAppWeb.Breadcrumb do
  use Phoenix.Component
  import MyAppWeb.CoreComponents

  attr :items, :list, required: true

  def breadcrumb(assigns) do
    assigns = assign(assigns, :size, length(assigns.items))

    ~H"""
    <nav class="flex" aria-label="breadcrumb">
      <ol class="inline-flex items-center space-x-1 md:space-x-3">
        <.breadcrumb_item
          :for={{item, index} <- Enum.with_index(@items)}
          type={index_to_item_type(index, @size)}
          navigate={item[:navigate]}
          text={item[:text]}
        />
      </ol>
    </nav>
    """
  end

  defp index_to_item_type(0, _size), do: "first"
  defp index_to_item_type(index, size) when index == size - 1, do: "last"
  defp index_to_item_type(_index, _size), do: "middle"

  attr :type, :string, default: "middle"
  attr :navigate, :string, default: "/"
  attr :text, :string, required: true

  defp breadcrumb_item(assigns) when assigns.type == "first" do
    ~H"""
    <li class="inline-flex items-center">
      <.link navigate={@navigate} class="inline-flex items-center text-sm font-medium">
        <.icon name="hero-home" class="h-4 w-4" />
      </.link>
    </li>
    """
  end

  defp breadcrumb_item(assigns) when assigns.type == "last" do
    ~H"""
    <li aria-current="page">
      <div class="flex items-center">
        <.icon name="hero-chevron-right" class="h-4 w-4" />
        <span class="ml-1 text-sm font-medium md:ml-2">
          <%= @text %>
        </span>
      </div>
    </li>
    """
  end

  defp breadcrumb_item(assigns) do
    ~H"""
    <li>
      <div class="flex items-center">
        <.icon name="hero-chevron-right" class="h-4 w-4" />
        <.link navigate={@navigate} class="ml-1 text-sm font-medium md:ml-2 ">
          <%= @text %>
        </.link>
      </div>
    </li>
    """
  end
end

breadcrumb/1

breadcrumb/1関数を唯一のパブリック関数としました。 これはパンくずリスト全体を取りまとめるコンポーネントです。コード読みやすくするために、パンくずリストアイテムは、breadcrumb_item/1という別のプライベートなコンポーネントに分割しました。breadcrumb_item/1はアサインされたアイテムタイプ(assigns.type)に応じて挙動を切り替えるようにしています。

breadcrumb_item/1

単純化すると、パンくずリストアイテムには3種類ある考えられ、それぞれ外観と動作が異なるようにする必要があります。

  1. 一番左(起点)
    • ホームのパス
    • リンクしたい
  2. 一番右(終点)
    • 現在のパス
    • リンク不要
  3. 間にある項目
    • 通過点のパス
    • リンクしたい

タイプごとにレンダリングするマークアップを切り替えます。

index_to_item_type/2

各アイテムのタイプについては、リストのインデックスと長さによって簡単に決定できます。リストの長さはさまざまなので、事前にリストの長さを調べておく必要があります。

Enum.with_index/2

Enum.with_index/2は、リストの各要素にインデックスを与えます。 項目タイプを決定する時にそのインデックスと事前に計算されたリストの長さを利用します。

6
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?