🍞

汎用パンクズリストコンポーネントを作ってみた(Ruby on Rails)

2024/10/27に公開

パンクズリストを汎用コンポーネントとして再利用できるようしました。
※前提として gem view_component の導入が必要です。

コード

# frozen_string_literal: true

class BreadcrumbsComponent < ViewComponent::Base
  def initialize(items:)
    super
    @items = items
    @data_scope_path = "components/#{self.class.name.underscore}"
  end

  def call
    content_tag(:nav, aria: { label: "breadcrumb" }, data: { scope_path: @data_scope_path }) do
      concat(content_tag(:ol, class: "breadcrumb", itemscope: "", itemtype: "https://schema.org/BreadcrumbList") do
        @items.each_with_index do |item, index|
          index == @items.size - 1 ? concat(last_breadcrumb_item(item, index)) : concat(breadcrumb_item(item, index))
        end
      end)
    end
  end

  private

  def last_breadcrumb_item(item, index)
    content_tag(
      :li,
      class: "breadcrumb-item active",
      aria: { current: "page" },
      itemprop: "itemListElement",
      itemscope: "",
      itemtype: "https://schema.org/ListItem"
    ) do
      concat(content_tag(:span, item[:name], itemprop: "name"))
      concat(tag.meta(itemprop: "position", content: (index + 1).to_s))
    end
  end

  def breadcrumb_item(item, index)
    content_tag(
      :li,
      class: "breadcrumb-item",
      itemprop: "itemListElement",
      itemscope: "",
      itemtype: "https://schema.org/ListItem"
    ) do
      concat(link_to(item[:path], itemprop: "item") do
        content_tag(:span, item[:name], itemprop: "name")
      end)
      concat(tag.meta(itemprop: "position", content: (index + 1).to_s))
      concat(content_tag(:span, "/", class: "arrow"))
    end
  end
end
[data-scope-path="components/breadcrumbs_component"] {
  .breadcrumb {
    display: flex;
    align-items: center;
    justify-content: flex-start;
    margin: 0;
    padding: 0;
    overflow-y: scroll;
    white-space: nowrap;
  }

  .breadcrumb-item {
    display: flex;
    align-items: center;
    justify-content: center;
    margin: 0;
    padding: 0;
    list-style: none;
  }

  .arrow {    
    display: block;
    margin: 0 0.6rem;
  }
}

使い方

<%= render(Application::Common::BreadcrumbsComponent.new(items: [
  { name: "TOP", path: root_path },
  { name: "Articles", path: articles_path },
  { name: @article.title, path: article_path(@article.id) }
])) %>

使いたい人はコピペしてどうぞ。

Discussion