refactor: extract product_grid to shared ShopComponents module
Create a reusable product grid component that handles responsive
column layouts. Supports two modes:
- Theme-based columns: Uses theme_settings.grid_columns for lg breakpoint
- Fixed columns: Use columns={:fixed_4} for 2/4 column layouts
Used in: home (featured), collection, pdp (related), error (suggestions)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
9746bf183c
commit
b2869514cb
@ -781,4 +781,82 @@ defmodule SimpleshopThemeWeb.ShopComponents do
|
||||
defp title_style(:featured), do: "color: var(--t-text-primary);"
|
||||
defp title_style(:compact), do: "font-family: var(--t-font-heading); color: var(--t-text-primary);"
|
||||
defp title_style(:minimal), do: "color: var(--t-text-primary);"
|
||||
|
||||
@doc """
|
||||
Renders a responsive product grid container.
|
||||
|
||||
This component wraps product cards in a responsive grid layout. It supports
|
||||
theme-based column settings or fixed column layouts for specific use cases.
|
||||
|
||||
## Attributes
|
||||
|
||||
* `theme_settings` - Optional. When provided, uses `grid_columns` setting for lg breakpoint.
|
||||
* `columns` - Optional. Fixed column count for lg breakpoint (overrides theme_settings).
|
||||
Use `:fixed_4` for a fixed 2/4 column layout (error page, related products).
|
||||
* `gap` - Optional. Gap size. Defaults to standard gap.
|
||||
* `class` - Optional. Additional CSS classes to add.
|
||||
|
||||
## Slots
|
||||
|
||||
* `inner_block` - Required. The product cards to render inside the grid.
|
||||
|
||||
## Examples
|
||||
|
||||
<.product_grid theme_settings={@theme_settings}>
|
||||
<%= for product <- @products do %>
|
||||
<.product_card product={product} theme_settings={@theme_settings} />
|
||||
<% end %>
|
||||
</.product_grid>
|
||||
|
||||
<.product_grid columns={:fixed_4} gap="gap-6">
|
||||
...
|
||||
</.product_grid>
|
||||
"""
|
||||
attr :theme_settings, :map, default: nil
|
||||
attr :columns, :atom, default: nil
|
||||
attr :gap, :string, default: nil
|
||||
attr :class, :string, default: nil
|
||||
|
||||
slot :inner_block, required: true
|
||||
|
||||
def product_grid(assigns) do
|
||||
~H"""
|
||||
<div class={grid_classes(@theme_settings, @columns, @gap, @class)}>
|
||||
<%= render_slot(@inner_block) %>
|
||||
</div>
|
||||
"""
|
||||
end
|
||||
|
||||
defp grid_classes(theme_settings, columns, gap, extra_class) do
|
||||
base = "product-grid grid"
|
||||
|
||||
cols =
|
||||
cond do
|
||||
columns == :fixed_4 ->
|
||||
"grid-cols-2 md:grid-cols-4"
|
||||
|
||||
theme_settings != nil ->
|
||||
responsive_cols = "grid-cols-1 sm:grid-cols-2"
|
||||
|
||||
lg_cols =
|
||||
case theme_settings.grid_columns do
|
||||
"2" -> "lg:grid-cols-2"
|
||||
"3" -> "lg:grid-cols-3"
|
||||
"4" -> "lg:grid-cols-4"
|
||||
_ -> "lg:grid-cols-3"
|
||||
end
|
||||
|
||||
"#{responsive_cols} #{lg_cols}"
|
||||
|
||||
true ->
|
||||
"grid-cols-1 sm:grid-cols-2 lg:grid-cols-3"
|
||||
end
|
||||
|
||||
gap_class = gap || ""
|
||||
|
||||
[base, cols, gap_class, extra_class]
|
||||
|> Enum.reject(&is_nil/1)
|
||||
|> Enum.reject(&(&1 == ""))
|
||||
|> Enum.join(" ")
|
||||
end
|
||||
end
|
||||
|
||||
@ -52,15 +52,7 @@
|
||||
</div>
|
||||
|
||||
<!-- Product Grid -->
|
||||
<div class={[
|
||||
"product-grid grid grid-cols-1 sm:grid-cols-2",
|
||||
case @theme_settings.grid_columns do
|
||||
"2" -> "lg:grid-cols-2"
|
||||
"3" -> "lg:grid-cols-3"
|
||||
"4" -> "lg:grid-cols-4"
|
||||
_ -> "lg:grid-cols-3"
|
||||
end
|
||||
]}>
|
||||
<.product_grid theme_settings={@theme_settings}>
|
||||
<%= for product <- @preview_data.products do %>
|
||||
<.product_card
|
||||
product={product}
|
||||
@ -70,7 +62,7 @@
|
||||
show_category={true}
|
||||
/>
|
||||
<% end %>
|
||||
</div>
|
||||
</.product_grid>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
|
||||
@ -40,7 +40,7 @@
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="product-grid mt-12 grid gap-4 grid-cols-2 md:grid-cols-4 max-w-xl mx-auto">
|
||||
<.product_grid columns={:fixed_4} gap="gap-4" class="mt-12 max-w-xl mx-auto">
|
||||
<%= for product <- Enum.take(@preview_data.products, 4) do %>
|
||||
<.product_card
|
||||
product={product}
|
||||
@ -49,7 +49,7 @@
|
||||
variant={:minimal}
|
||||
/>
|
||||
<% end %>
|
||||
</div>
|
||||
</.product_grid>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
|
||||
@ -52,15 +52,7 @@
|
||||
Featured products
|
||||
</h2>
|
||||
|
||||
<div class={[
|
||||
"product-grid grid grid-cols-1 sm:grid-cols-2",
|
||||
case @theme_settings.grid_columns do
|
||||
"2" -> "lg:grid-cols-2"
|
||||
"3" -> "lg:grid-cols-3"
|
||||
"4" -> "lg:grid-cols-4"
|
||||
_ -> "lg:grid-cols-3"
|
||||
end
|
||||
]}>
|
||||
<.product_grid theme_settings={@theme_settings}>
|
||||
<%= for product <- Enum.take(@preview_data.products, 4) do %>
|
||||
<.product_card
|
||||
product={product}
|
||||
@ -69,7 +61,7 @@
|
||||
variant={:featured}
|
||||
/>
|
||||
<% end %>
|
||||
</div>
|
||||
</.product_grid>
|
||||
|
||||
<div class="text-center mt-8">
|
||||
<button
|
||||
|
||||
@ -402,7 +402,7 @@
|
||||
You might also like
|
||||
</h2>
|
||||
|
||||
<div class="product-grid grid gap-6 grid-cols-2 md:grid-cols-4">
|
||||
<.product_grid columns={:fixed_4} gap="gap-6">
|
||||
<%= for related_product <- Enum.slice(@preview_data.products, 1, 4) do %>
|
||||
<.product_card
|
||||
product={related_product}
|
||||
@ -411,7 +411,7 @@
|
||||
variant={:compact}
|
||||
/>
|
||||
<% end %>
|
||||
</div>
|
||||
</.product_grid>
|
||||
</div>
|
||||
<% end %>
|
||||
</main>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user