Current section

Files

Jump to
layout_builder lib layout_builder.ex
Raw

lib/layout_builder.ex

defmodule LayoutBuilder do
use Phoenix.LiveComponent
use PetalComponents
import LayoutBuilder.Gettext
alias LayoutBuilder.Layout.Layout
@moduledoc """
`LayoutBuilder` allows you to create a %Layout.Layout{} containing %Layout.Row{} and %Layout.Row{}.
"""
@doc """
LayoutBuilder component
## Examples
<.layout_builder />
"""
slot :content, doc: "Content to show inside each col" do
attr :class, :string, doc: "Custom classes to add"
end
attr :title, :string, default: "h2", doc: "<hx> title level", values: ["h1", "h2", "h3", "h4", "h5", "h6"]
attr :title_class, :string, default: "", doc: "classes to add to <hx>", examples: ["text-2xl font-medium"]
attr :class, :string, default: "", doc: "classes to pass to layout builder", examples: ["mt-6"]
attr :row_class, :string, default: "flex", doc: "classes to pass to layout builder", examples: ["mt-6"]
attr :edit_layout, :boolean, default: true, doc: "show buttons to add / remove cols and rows from layout builder and show background colors"
attr :layout, Layout, required: true, doc: "layout built", examples: [%Layout{}]
def render(assigns) do
assigns = assigns
|> assign_new(:above, fn -> nil end)
~H"""
<article class={@class}>
<%= case @title do %>
<% "h1" -> %>
<h1 class={"#{classes()} #{@title_class}"}><.title/></h1>
<% "h2" -> %>
<h2 class={"#{classes()} #{@title_class}"}><.title/></h2>
<% "h3" -> %>
<h3 class={"#{classes()} #{@title_class}"}><.title/></h3>
<% "h4" -> %>
<h4 class={"#{classes()} #{@title_class}"}><.title/></h4>
<% "h5" -> %>
<h5 class={"#{classes()} #{@title_class}"}><.title/></h5>
<% "h6" -> %>
<h6 class={"#{classes()} #{@title_class}"}><.title/></h6>
<% end %>
<div class="mx-auto mt-1 gap-x-6 gap-y-6 flex">
<div class="max-w-3xl w-full gap-x-4 gap-y-4 flex flex-col bg-white dark:bg-black p-6">
<%= for row <- @layout.rows do %>
<%= if @above do %>
<div class="flex flex-wrap">
<%= render_slot(@above, row) %>
</div>
<% end %>
<div class={if @edit_layout do "bg-gray-100 dark:bg-gray-900 rounded-lg " else "" end <> "#{@row_class} gap-x-3 p-3"}>
<%= for col <- row.cols do %>
<div class={if @edit_layout do "rounded-lg bg-gray-200 dark:bg-gray-800 " else "" end <> Map.get(col, :class, "flex-1")}>
<%= render_slot(@content, col) %>
</div>
<% end %>
<%= if @edit_layout do %>
<.button color="gray" class="w-20 h-12" phx-value-row={row.id} phx-click="add_column" phx-target={@myself}>
<Heroicons.plus class="w-6 h-6" />
</.button>
<% end %>
</div>
<% end %>
<%= if @edit_layout do %>
<.button color="gray" class="w-full h-12" phx-click="add_row" phx-target={@myself}>
<Heroicons.plus class="w-6 h-6" />
</.button>
<% end %>
</div>
</div>
</article>
"""
end
@spec handle_event(String.t(), map, Elixir.Phoenix.LiveView.Socket.t()) :: {:noreply, Elixir.Phoenix.LiveView.Socket.t()}
def handle_event("add_column", %{"row" => id}, socket) do
row = Enum.find(socket.assigns.layout.rows, fn row -> row.id == id end)
layout = Layout.add_col(socket.assigns.layout, row)
send(self(), %{layout: layout})
{:noreply, socket}
end
def handle_event("add_row", _, socket) do
layout = Layout.add_row(socket.assigns.layout)
send(self(), %{layout: layout})
{:noreply, assign(socket, :layout, layout)}
end
defp title(assigns) do
~H"""
<%= gettext("Layout builder") %>
"""
end
defp classes do
"text-sm font-medium text-gray-500 dark:text-gray-400"
end
end