Packages
petal_components
0.17.7
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_components/button.ex
defmodule PetalComponents.Button do
use Phoenix.Component
alias PetalComponents.Loading
alias PetalComponents.Heroicons
alias PetalComponents.Link
import PetalComponents.Helpers
# prop class, :string
# prop color, :string, options: ["primary", "secondary", "info", "success", "warning", "danger", "gray"]
# prop link_type, :string, options: ["button", "a", "live_patch", "live_redirect"]
# prop label, :string
# prop size, :string
# prop variant, :string
# prop to, :string
# prop loading, :boolean, default: false
# prop disabled, :boolean, default: false
# slot default
def button(assigns) do
assigns =
assigns
|> assign_new(:link_type, fn -> "button" end)
|> assign_new(:inner_block, fn -> nil end)
|> assign_new(:loading, fn -> false end)
|> assign_new(:size, fn -> "md" end)
|> assign_new(:disabled, fn -> false end)
|> assign_new(:rest, fn -> get_rest(assigns) end)
|> assign_new(:classes, fn -> button_classes(assigns) end)
|> assign_new(:class, fn -> "" end)
|> assign_new(:to, fn -> nil end)
~H"""
<Link.link to={@to} link_type={@link_type} class={@classes} disabled={@disabled} {@rest}>
<%= if @loading do %>
<Loading.spinner show={true} size_class={get_spinner_size_classes(@size)} />
<% end %>
<%= if @inner_block do %>
<%= render_slot(@inner_block) %>
<% else %>
<%= @label %>
<% end %>
</Link.link>
"""
end
# prop class, :string
# prop color, :string, options: ["primary", "secondary", "info", "success", "warning", "danger", "gray"]
# prop link_type, :string, options: ["button", "a", "live_patch", "live_redirect"]
# prop label, :string
# prop size, :string
# prop variant, :string
# prop to, :string
# prop loading, :boolean, default: false
# prop disabled, :boolean, default: false
# slot default
def icon_button(assigns) do
assigns =
assigns
|> assign_new(:link_type, fn -> "button" end)
|> assign_new(:inner_block, fn -> nil end)
|> assign_new(:loading, fn -> false end)
|> assign_new(:size, fn -> "sm" end)
|> assign_new(:disabled, fn -> false end)
|> assign_new(:rest, fn -> get_rest(assigns) end)
|> assign_new(:class, fn -> "" end)
|> assign_new(:to, fn -> nil end)
|> assign_new(:color, fn -> "gray" end)
~H"""
<Link.link
to={@to}
link_type={@link_type}
class={build_class(
[
"rounded-full p-2 inline-block",
get_disabled_classes(@disabled),
get_icon_button_background_color_classes(@color),
get_icon_button_color_classes(@color),
@class
])}
disabled={@disabled}
{@rest}
>
<%= if @loading do %>
<Loading.spinner show={true} size_class={get_spinner_size_classes(@size)} />
<% else %>
<Heroicons.Solid.render icon={@icon} class={build_class(
[
get_icon_button_size_classes(@size)
])}/>
<% end %>
</Link.link>
"""
end
defp button_classes(opts) do
opts = %{
size: opts[:size] || "md",
variant: opts[:variant] || "solid",
color: opts[:color] || "primary",
loading: opts[:loading] || false,
disabled: opts[:disabled] || false,
icon: opts[:icon] || false,
user_added_classes: opts[:class] || ""
}
color_css = get_color_classes(opts)
size_css =
case opts[:size] do
"xs" -> "text-xs leading-4 px-2.5 py-1.5"
"sm" -> "text-sm leading-4 px-3 py-2"
"md" -> "text-sm leading-5 px-4 py-2"
"lg" -> "text-base leading-6 px-4 py-2"
"xl" -> "text-base leading-6 px-6 py-3"
end
loading_css =
if opts[:loading] do
"flex gap-2 items-center whitespace-nowrap disabled cursor-not-allowed"
else
""
end
icon_css =
if opts[:icon] do
"flex gap-2 items-center whitespace-nowrap"
else
""
end
[
opts.user_added_classes,
color_css,
size_css,
loading_css,
get_disabled_classes(opts[:disabled]),
icon_css,
"font-medium rounded-md inline-flex items-center justify-center border focus:outline-none transition duration-150 ease-in-out"
]
|> build_class()
end
defp get_rest(assigns) do
assigns_to_attributes(assigns, [
:loading,
:disabled,
:link_type,
:size,
:variant,
:color,
:icon,
:class,
:to
])
end
defp get_color_classes(%{color: "primary", variant: variant}) do
case variant do
"outline" ->
"border-primary-400 dark:border-primary-400 dark:hover:border-primary-300 dark:hover:text-primary-300 dark:hover:bg-transparent dark:text-primary-400 hover:border-primary-600 text-primary-600 hover:text-primary-700 active:bg-primary-200 hover:bg-primary-50 focus:border-primary-700 focus:shadow-outline-primary"
"inverted" ->
"border-primary-400 dark:border-primary-400 dark:hover:border-primary-700 dark:hover:text-white dark:hover:bg-primary-700 dark:text-primary-400 hover:border-primary-600 text-primary-600 hover:text-white active:bg-primary-700 hover:bg-primary-600 focus:border-primary-700 focus:shadow-outline-primary"
"shadow" ->
"shadow-xl border-transparent text-white bg-primary-600 active:bg-primary-700 hover:bg-primary-700 focus:bg-primary-700 active:bg-primary-800 focus:shadow-outline-primary shadow-primary-500/30 dark:hover:shadow-primary-600/30 dark:focus:shadow-primary-600/30 dark:active:shadow-primary-700/30"
_ ->
"border-transparent text-white bg-primary-600 active:bg-primary-700 hover:bg-primary-700 focus:bg-primary-700 active:bg-primary-800 focus:shadow-outline-primary"
end
end
defp get_color_classes(%{color: "secondary", variant: variant}) do
case variant do
"outline" ->
"border-secondary-400 dark:border-secondary-400 dark:hover:border-secondary-300 dark:hover:text-secondary-300 dark:hover:bg-transparent dark:text-secondary-400 hover:border-secondary-600 text-secondary-600 hover:text-secondary-700 active:bg-secondary-200 hover:bg-secondary-50 focus:border-secondary-700 focus:shadow-outline-secondary"
"inverted" ->
"border-secondary-400 dark:border-secondary-400 dark:hover:border-secondary-700 dark:hover:text-white dark:hover:bg-secondary-700 dark:text-secondary-400 hover:border-secondary-600 text-secondary-600 hover:text-white active:bg-secondary-700 hover:bg-secondary-600 focus:border-secondary-700 focus:shadow-outline-secondary"
"shadow" ->
"shadow-xl border-transparent text-white bg-secondary-600 active:bg-secondary-700 hover:bg-secondary-700 focus:bg-secondary-700 active:bg-secondary-800 focus:shadow-outline-secondary shadow-secondary-500/30 dark:hover:shadow-secondary-600/30 dark:focus:shadow-secondary-600/30 dark:active:shadow-secondary-700/30"
_ ->
"border-transparent text-white bg-secondary-600 active:bg-secondary-700 hover:bg-secondary-700 focus:bg-secondary-700 active:bg-secondary-800 focus:shadow-outline-secondary"
end
end
defp get_color_classes(%{color: "white", variant: variant}) do
case variant do
"outline" ->
"border-gray-400 dark:border-gray-300 dark:hover:border-gray-200 dark:hover:text-gray-200 dark:hover:bg-transparent dark:text-gray-300 hover:border-gray-600 text-gray-600 hover:text-gray-700 active:bg-gray-100 hover:bg-gray-50 focus:bg-gray-50 focus:border-gray-500 active:border-gray-600"
"inverted" ->
"border-gray-400 dark:border-white dark:hover:border-gray-700 dark:hover:text-black dark:hover:bg-white dark:text-gray-300 hover:border-gray-600 text-gray-600 hover:text-black active:bg-gray-700 hover:bg-white focus:border-gray-700 focus:shadow-outline-gray"
"shadow" ->
"shadow-xl text-gray-700 bg-white border-gray-300 hover:text-gray-900 hover:text-gray-900 hover:border-gray-400 hover:bg-gray-50 focus:outline-none focus:border-gray-400 focus:bg-gray-100 focus:text-gray-900 active:border-gray-400 active:bg-gray-200 dark:bg-white dark:hover:bg-gray-200 dark:hover:border-transparent dark:border-transparent active:text-black shadow-gray-500/30 dark:shadow-gray-200/30 dark:hover:shadow-gray-300/30 dark:focus:shadow-gray-300/30 dark:active:shadow-gray-400/30"
_ ->
"text-gray-700 bg-white border-gray-300 hover:text-gray-900 hover:text-gray-900 hover:border-gray-400 hover:bg-gray-50 focus:outline-none focus:border-gray-400 focus:bg-gray-100 focus:text-gray-900 active:border-gray-400 active:bg-gray-200 active:text-black dark:bg-white dark:hover:bg-gray-200 dark:hover:border-transparent dark:border-transparent"
end
end
defp get_color_classes(%{color: "pure_white", variant: variant}) do
case variant do
_ ->
"text-gray-700 bg-white border-transparent border-white hover:text-gray-900 hover:text-gray-900 hover:border-transparent hover:bg-gray-50 focus:outline-none focus:border-transparent focus:bg-gray-100 focus:text-gray-900 active:border-transparent active:bg-gray-200 active:text-black dark:bg-white dark:hover:bg-gray-200 dark:hover:border-transparent dark:border-transparent"
end
end
defp get_color_classes(%{color: "info", variant: variant}) do
case variant do
"outline" ->
"border-blue-400 dark:border-blue-400 dark:hover:border-blue-300 dark:hover:text-blue-300 dark:hover:bg-transparent dark:text-blue-400 hover:border-blue-600 text-blue-600 hover:text-blue-700 active:border-blue-600 focus:text-blue-600 active:text-blue-700 active:bg-blue-100 hover:bg-blue-50 focus:border-blue-700"
"inverted" ->
"border-blue-400 dark:border-blue-400 dark:hover:border-blue-700 dark:hover:text-white dark:hover:bg-blue-700 dark:text-blue-400 hover:border-blue-600 text-blue-600 hover:text-white active:bg-blue-700 hover:bg-blue-600 focus:border-blue-700 focus:shadow-outline-blue"
"shadow" ->
"shadow-xl border-transparent text-white bg-blue-600 active:bg-blue-700 hover:bg-blue-700 focus:bg-blue-700 active:bg-blue-800 focus:shadow-outline-blue shadow-blue-500/30 dark:hover:shadow-blue-600/30 dark:focus:shadow-blue-600/30 dark:active:shadow-blue-700/30"
_ ->
"border-transparent text-white bg-blue-600 active:bg-blue-700 hover:bg-blue-700 active:bg-blue-700 focus:bg-blue-700"
end
end
defp get_color_classes(%{color: "success", variant: variant}) do
case variant do
"outline" ->
"border-green-400 dark:border-green-400 dark:hover:border-green-300 dark:hover:text-green-300 dark:hover:bg-transparent dark:text-green-400 hover:border-green-600 text-green-600 hover:text-green-700 active:border-green-600 focus:text-green-600 active:text-green-700 active:bg-green-100 hover:bg-green-50 focus:border-green-700"
"inverted" ->
"border-green-400 dark:border-green-400 dark:hover:border-green-700 dark:hover:text-white dark:hover:bg-green-700 dark:text-green-400 hover:border-green-600 text-green-600 hover:text-white active:bg-green-700 hover:bg-green-600 focus:border-green-700 focus:shadow-outline-green"
"shadow" ->
"shadow-xl border-transparent text-white bg-green-600 active:bg-green-700 hover:bg-green-700 focus:bg-green-700 active:bg-green-800 focus:shadow-outline-green shadow-green-500/30 dark:hover:shadow-green-600/30 dark:focus:shadow-green-600/30 dark:active:shadow-green-700/30"
_ ->
"border-transparent text-white bg-green-600 active:bg-green-700 hover:bg-green-700 active:bg-green-700 focus:bg-green-700"
end
end
defp get_color_classes(%{color: "warning", variant: variant}) do
case variant do
"outline" ->
"border-yellow-400 dark:border-yellow-400 dark:hover:border-yellow-300 dark:hover:text-yellow-300 dark:hover:bg-transparent dark:text-yellow-400 hover:border-yellow-600 text-yellow-600 hover:text-yellow-700 active:border-yellow-600 focus:text-yellow-600 active:text-yellow-700 active:bg-yellow-100 hover:bg-yellow-50 focus:border-yellow-700"
"inverted" ->
"border-yellow-400 dark:border-yellow-400 dark:hover:border-yellow-700 dark:hover:text-white dark:hover:bg-yellow-700 dark:text-yellow-400 hover:border-yellow-600 text-yellow-600 hover:text-white active:bg-yellow-700 hover:bg-yellow-600 focus:border-yellow-700 focus:shadow-outline-yellow"
"shadow" ->
"shadow-xl border-transparent text-white bg-yellow-600 active:bg-yellow-700 hover:bg-yellow-700 focus:bg-yellow-700 active:bg-yellow-800 focus:shadow-outline-yellow shadow-yellow-500/30 dark:hover:shadow-yellow-600/30 dark:focus:shadow-yellow-600/30 dark:active:shadow-yellow-700/30"
_ ->
"border-transparent text-white bg-yellow-600 active:bg-yellow-700 hover:bg-yellow-700 active:bg-yellow-700 focus:bg-yellow-700"
end
end
defp get_color_classes(%{color: "danger", variant: variant}) do
case variant do
"outline" ->
"border-red-400 dark:border-red-400 dark:hover:border-red-300 dark:hover:text-red-300 dark:hover:bg-transparent dark:text-red-400 hover:border-red-600 text-red-600 hover:text-red-700 active:bg-red-200 active:border-red-700 hover:bg-red-50 focus:border-red-700"
"inverted" ->
"border-red-400 dark:border-red-400 dark:hover:border-red-700 dark:hover:text-white dark:hover:bg-red-700 dark:text-red-400 hover:border-red-600 text-red-600 hover:text-white active:bg-red-700 hover:bg-red-600 focus:border-red-700 focus:shadow-outline-red"
"shadow" ->
"shadow-xl border-transparent text-white bg-red-600 active:bg-red-700 hover:bg-red-700 focus:bg-red-700 active:bg-red-800 focus:shadow-outline-red shadow-red-500/30 dark:hover:shadow-red-600/30 dark:focus:shadow-red-600/30 dark:active:shadow-red-700/30"
_ ->
"border-transparent text-white bg-red-600 active:bg-red-700 hover:bg-red-700 active:bg-green-700 focus:bg-red-700"
end
end
defp get_color_classes(%{color: "gray", variant: variant}) do
case variant do
"outline" ->
"border-gray-400 dark:border-gray-400 dark:hover:border-gray-300 dark:hover:text-gray-300 dark:hover:bg-transparent dark:text-gray-400 hover:border-gray-600 text-gray-600 hover:text-gray-700 active:bg-gray-200 active:border-gray-700 hover:bg-gray-50 focus:border-gray-700"
"inverted" ->
"border-gray-400 dark:border-gray-400 dark:hover:border-gray-700 dark:hover:text-white dark:hover:bg-gray-700 dark:text-gray-400 hover:border-gray-600 text-gray-600 hover:text-white active:bg-gray-700 hover:bg-gray-600 focus:border-gray-700 focus:shadow-outline-gray"
"shadow" ->
"shadow-xl border-transparent text-white bg-gray-600 active:bg-gray-700 hover:bg-gray-700 focus:bg-gray-700 active:bg-gray-800 focus:shadow-outline-gray shadow-gray-500/30 dark:hover:shadow-gray-600/30 dark:focus:shadow-gray-600/30 dark:active:shadow-gray-700/30"
_ ->
"border-transparent text-white bg-gray-600 active:bg-gray-700 hover:bg-gray-700 active:bg-green-700 focus:bg-gray-700"
end
end
defp get_spinner_size_classes("xs"), do: "h-3 w-3"
defp get_spinner_size_classes("sm"), do: "h-4 w-4"
defp get_spinner_size_classes("md"), do: "h-5 w-5"
defp get_spinner_size_classes("lg"), do: "h-5 w-5"
defp get_spinner_size_classes("xl"), do: "h-6 w-6"
defp get_icon_button_size_classes("xs"), do: "w-4 h-4"
defp get_icon_button_size_classes("sm"), do: "w-5 h-5"
defp get_icon_button_size_classes("md"), do: "w-6 h-6"
defp get_icon_button_size_classes("lg"), do: "w-7 h-7"
defp get_icon_button_size_classes("xl"), do: "w-8 h-8"
defp get_icon_button_color_classes("primary"), do: "text-primary-600 dark:text-primary-500"
defp get_icon_button_color_classes("secondary"),
do: "text-secondary-600 dark:text-secondary-500"
defp get_icon_button_color_classes("gray"), do: "text-gray-600 dark:text-gray-500"
defp get_icon_button_color_classes("info"), do: "text-blue-600 dark:text-blue-500"
defp get_icon_button_color_classes("success"), do: "text-green-600 dark:text-green-500"
defp get_icon_button_color_classes("warning"), do: "text-yellow-600 dark:text-yellow-500"
defp get_icon_button_color_classes("danger"), do: "text-red-600 dark:text-red-500"
defp get_icon_button_background_color_classes("primary"),
do: "hover:bg-primary-50 dark:hover:bg-gray-800"
defp get_icon_button_background_color_classes("secondary"),
do: "hover:bg-secondary-50 dark:hover:bg-gray-800"
defp get_icon_button_background_color_classes("gray"),
do: "hover:bg-gray-100 dark:hover:bg-gray-800"
defp get_icon_button_background_color_classes("info"),
do: "hover:bg-blue-50 dark:hover:bg-gray-800"
defp get_icon_button_background_color_classes("success"),
do: "hover:bg-green-50 dark:hover:bg-gray-800"
defp get_icon_button_background_color_classes("warning"),
do: "hover:bg-yellow-50 dark:hover:bg-gray-800"
defp get_icon_button_background_color_classes("danger"),
do: "hover:bg-red-50 dark:hover:bg-gray-800"
defp get_disabled_classes(true), do: "disabled cursor-not-allowed opacity-50"
defp get_disabled_classes(false), do: ""
end