Packages
petal_components
0.2.2
4.7.0
4.6.2
4.6.1
4.6.0
4.5.0
4.4.0
4.3.0
4.2.1
4.2.0
4.1.2
4.1.1
4.1.0
4.0.12
4.0.11
4.0.10
4.0.6
4.0.5
4.0.4
4.0.3
4.0.1
3.2.2
3.2.1
3.2.0
3.1.0
3.0.2
3.0.1
3.0.0
2.9.3
2.9.2
retired
2.9.1
retired
2.9.0
retired
2.8.4
2.8.3
2.8.2
2.8.1
2.8.0
2.7.4
2.7.3
2.7.2
2.7.1
2.7.0
2.6.1
2.6.0
2.5.2
2.5.1
2.5.0
2.4.3
2.4.2
2.4.1
2.4.0
2.3.0
2.2.1
2.2.0
2.1.2
2.1.1
2.1.0
2.0.6
2.0.5
2.0.4
2.0.3
2.0.2
2.0.1
2.0.0
1.9.3
1.9.2
1.9.1
1.9.0
1.8.0
1.7.1
1.7.0
1.6.2
1.6.1
1.6.0
1.5.5
1.5.4
1.5.3
1.5.2
1.5.1
1.5.0
1.4.9
1.4.8
1.4.7
1.4.6
1.4.5
1.4.4
1.4.3
1.4.2
1.4.1
1.4.0
1.3.0
1.2.14
1.2.13
1.2.12
1.2.11
1.2.10
1.2.9
1.2.8
1.2.7
1.2.6
1.2.5
1.2.4
1.2.3
1.2.2
1.2.1
1.2.0
1.1.6
1.1.5
1.1.4
1.1.3
1.1.2
1.1.1
1.1.0
1.0.8
1.0.7
1.0.6
1.0.5
1.0.4
1.0.3
1.0.2
1.0.1
1.0.0
0.19.10
0.19.9
0.19.8
0.19.7
0.19.6
0.19.5
0.19.4
0.19.3
0.19.2
0.19.1
0.19.0
0.18.5
0.18.4
0.18.3
0.18.2
0.18.1
0.18.0
0.17.7
0.17.6
0.17.5
0.17.4
0.17.3
0.17.2
0.17.1
0.17.0
0.16.0
0.15.0
0.14.1
0.14.0
0.13.7
0.13.6
0.13.5
0.13.4
0.13.3
0.13.2
0.13.1
0.13.0
0.12.0
0.11.4
0.11.3
0.11.2
0.11.1
0.11.0
0.10.8
0.10.7
0.10.6
0.10.5
0.10.4
0.10.3
0.10.2
0.10.1
0.10.0
0.9.3
0.9.2
0.9.1
0.9.0
0.8.0
0.7.0
0.6.1
0.6.0
0.5.1
0.5.0
0.4.0
0.3.2
0.3.1
0.3.0
0.2.2
0.2.1
0.2.0
0.1.0
Shadcn-style Phoenix LiveView components that AI assistants can actually use. Pair with the MCP server so AI coding tools can inspect the real component API.
Current section
Files
Jump to
Current section
Files
lib/petal/form.ex
defmodule PetalComponents.Form do
use Phoenix.Component
import Phoenix.HTML.Form
@moduledoc """
Everything related to forms: inputs, labels etc
"""
def plain_label(assigns) do
~H"""
<label class={label_classes()}>
<%= render_slot(@inner_block) %>
</label>
"""
end
def form_label(assigns) do
~H"""
<%= label @form, @field, class: label_classes(assigns) do %>
<%= render_slot(@inner_block) %>
<% end %>
"""
end
def text_input(assigns), do: input(Map.put(assigns, :type, "text"))
def textarea(assigns), do: input(Map.put(assigns, :type, "textarea"))
def select(assigns), do: input(Map.put(assigns, :type, "select"))
def checkbox(assigns), do: input(Map.put(assigns, :type, "checkbox"))
def radios(assigns), do: input(Map.put(assigns, :type, "radios"))
defp input(assigns) do
assigns = assign_new(assigns, :label, fn -> humanize(assigns.field) end)
assigns = assign_new(assigns, :type, fn -> "text" end)
assigns =
assign_new(assigns, :input_attributes, fn ->
Map.drop(assigns, [
:label,
:form,
:field,
:type,
:options,
:__changed__
])
|> Map.to_list()
end)
build_input(assigns)
end
defp build_input(%{type: "text"} = assigns) do
~H"""
<div class="mb-6">
<.form_label form={@form} field={@field} type={@type}>
<%= @label %>
</.form_label>
<%= text_input @form, @field, [class: text_input_classes(assigns)] ++ @input_attributes %>
</div>
"""
end
defp build_input(%{type: "textarea"} = assigns) do
~H"""
<div class="mb-6">
<.form_label form={@form} field={@field} type={@type}>
<%= @label %>
</.form_label>
<%= textarea @form, @field, Keyword.merge([
class: text_input_classes(assigns),
rows: "4"
], @input_attributes) %>
</div>
"""
end
defp build_input(%{type: "select"} = assigns) do
~H"""
<div class="mb-6">
<.form_label form={@form} field={@field} type={@type}>
<%= @label %>
</.form_label>
<%= select @form, @field, @options, [class: select_classes(assigns)] ++ @input_attributes %>
</div>
"""
end
defp build_input(%{type: "checkbox"} = assigns) do
~H"""
<div class="flex items-start mb-6">
<div class="flex items-center h-5 ">
<%= checkbox @form, @field, [class: checkbox_classes()] ++ @input_attributes %>
</div>
<div class="ml-3 text-sm">
<.form_label form={@form} field={@field} type={@type}>
<%= @label %>
</.form_label>
</div>
</div>
"""
end
defp build_input(%{type: "radios"} = assigns) do
~H"""
<.plain_label><%= @label %></.plain_label>
<%= for {label, value} <- @options do %>
<div class="flex items-start mb-1">
<div class="flex items-center h-5">
<%= radio_button @form, @field, value, [class: radio_classes()] ++ @input_attributes %>
</div>
<div class="ml-3 text-sm">
<.form_label form={@form} field={@field} type={"radio"}>
<%= label %>
</.form_label>
</div>
</div>
<% end %>
"""
end
defp label_classes(assigns \\ %{}) do
type_classes =
if Enum.member?(["checkbox", "radio"], assigns[:type]) do
""
else
"mb-2 font-medium"
end
"#{type_classes} text-sm text-gray-900 block dark:text-gray-200"
end
defp text_input_classes(assigns) do
"#{assigns[:class] || ""} sm:text-sm block shadow-sm w-full rounded-md border-gray-300 dark:bg-gray-800 dark:text-gray-300 dark:border-gray-600 focus:border-primary-500 dark:focus:border-primary-500 focus:outline-none focus:ring-primary-500 focus:border-primary-500"
end
defp select_classes(assigns) do
"#{assigns[:class] || ""} block w-full pl-3 pr-10 py-2 text-base border-gray-300 focus:outline-none focus:ring-primary-500 focus:border-primary-500 sm:text-sm rounded-md"
end
defp checkbox_classes() do
"border-gray-300 rounded text-primary-700 w-5 h-5 ease-linear transition-all duration-150"
end
defp radio_classes() do
"h-4 w-4 mt-0.5 cursor-pointer text-primary-600 border-gray-300 focus:ring-primary-500"
end
end