Packages
petal_components
0.13.5
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_components/avatar.ex
defmodule PetalComponents.Avatar do
use Phoenix.Component
alias PetalComponents.Heroicons
# prop src, :string
# prop size, :string
def avatar(assigns) do
assigns =
assigns
|> assign_new(:src, fn -> nil end)
|> assign_new(:size, fn -> "md" end)
|> assign_new(:class, fn -> "" end)
|> assign_new(:name, fn -> nil end)
|> assign_new(:random_color, fn -> false end)
|> assign_new(:extra_assigns, fn ->
assigns_to_attributes(assigns, ~w(
src
size
class
name
random_color
)a)
end)
~H"""
<%= if src_blank?(@src) && !@name do %>
<div {@extra_assigns} class={Enum.join([
"inline-block relative overflow-hidden bg-gray-100 dark:bg-gray-700 rounded-full",
get_size_classes(@size),
@class
], " ")}>
<Heroicons.Solid.user class="relative w-full h-full text-gray-300 dark:text-gray-300 dark:bg-gray-700 top-[12%] scale-[1.15] transform" />
</div>
<% else %>
<%= if !@src && @name do %>
<div {@extra_assigns}
style={maybe_generate_random_color(@random_color, @name)}
class={Enum.join([
"flex items-center justify-center bg-gray-100 dark:bg-gray-700 rounded-full font-semibold uppercase text-gray-500 dark:text-gray-300",
get_size_classes(@size),
@class,
], " ")}
>
<%= generate_initials(@name) %>
</div>
<% else %>
<img {@extra_assigns} src={@src} class={Enum.join([
"rounded-full object-cover",
get_size_classes(@size),
@class
], " ")} />
<% end %>
<% end %>
"""
end
def avatar_group(assigns) do
assigns =
assigns
|> assign_new(:classes, fn -> avatar_group_classes(assigns) end)
|> assign_new(:extra_assigns, fn ->
assigns_to_attributes(assigns, ~w(classes)a)
end)
~H"""
<div {@extra_assigns} class={@classes}>
<%= for src <- @avatars do %>
<.avatar src={src} size={@size} class="ring-white ring-2 dark:ring-gray-100" />
<% end %>
</div>
"""
end
defp get_size_classes(size) do
case size do
"xs" -> "w-7 h-7 text-xs"
"sm" -> "w-8 h-8 text-sm"
"md" -> "w-10 h-10 text-md"
"lg" -> "w-12 h-12 text-lg"
"xl" -> "w-14 h-14 text-xl"
end
end
defp avatar_group_classes(opts) do
opts = %{
size: opts[:size] || "md",
class: opts[:class] || ""
}
size_css =
case opts[:size] do
"xs" -> "relative z-0 flex -space-x-2"
"sm" -> "relative z-0 flex -space-x-3"
"md" -> "relative z-0 flex -space-x-4"
"lg" -> "relative z-0 flex -space-x-5"
"xl" -> "relative z-0 flex -space-x-6"
end
"""
#{opts.class}
#{size_css}
"""
end
defp src_blank?(src) do
!src || src == ""
end
defp maybe_generate_random_color(false, _), do: nil
defp maybe_generate_random_color(true, name) do
"background-color: #{generate_color_from_string(name)}; color: white;"
end
defp generate_color_from_string(string) do
a_number =
string
|> String.to_charlist()
|> Enum.reduce(0, fn x, acc -> x + acc end)
"hsl(#{rem(a_number, 360)}, 100%, 35%)"
end
defp generate_initials(name) when is_binary(name) do
word_array = String.split(name)
if length(word_array) == 1 do
List.first(word_array)
|> String.slice(0..1)
|> String.upcase()
else
initial1 = String.first(List.first(word_array))
initial2 = String.first(List.last(word_array))
String.upcase(initial1 <> initial2)
end
end
defp generate_initials(_) do
""
end
end