Packages
petal_components
4.0.10
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/helpers.ex
defmodule PetalComponents.Helpers do
@moduledoc """
For any helper functions used across multiple components. Ideally we keep this empty - components should be copy-pastable.
"""
alias Phoenix.LiveView.JS
@doc """
Composes two `Phoenix.LiveView.JS` structs by concatenating their operations.
User JS operations execute first, followed by component JS operations.
"""
def compose_js(%JS{ops: []}, component_js), do: component_js
def compose_js(user_js, %JS{ops: []}), do: user_js
def compose_js(%JS{ops: user_ops}, %JS{ops: component_ops}),
do: %JS{ops: user_ops ++ component_ops}
@doc """
Generates a unique HTML ID based on the given string or iodata.
## Parameters
- input: The input string or iodata (e.g., heading or label)
- prefix: An optional prefix for the ID (default: "c")
## Examples
iex> PetalComponents.Helpers.uniq_id("My Heading")
"c-my-heading-1234"
iex> PetalComponents.Helpers.uniq_id(["My ", {:safe, "<a>Link</a>"}, " Label"], "custom")
"custom-my-link-label-5678"
"""
def uniq_id(input, prefix \\ "c")
def uniq_id(input, prefix) when is_list(input) do
# Convert iodata to string, handling both plain lists and lists with safe HTML
string =
input
|> Enum.map(fn
{:safe, html} ->
html
|> IO.iodata_to_binary()
|> strip_html_and_entities()
x when is_binary(x) ->
x
x ->
to_string(x)
end)
|> Enum.join("")
create_slug(string, prefix)
end
def uniq_id(string, prefix) when is_binary(string) do
create_slug(string, prefix)
end
def uniq_id(other, prefix) do
create_slug(to_string(other), prefix)
end
defp create_slug(string, prefix) do
slug =
string
|> String.downcase()
|> String.replace(~r/[^\w-]+/, "-")
|> String.slice(0, 20)
unique = System.unique_integer([:positive]) |> Integer.to_string(36)
"#{prefix}-#{slug}-#{unique}"
end
defp strip_html_and_entities(string) do
string
# Strip HTML tags
|> String.replace(~r/<[^>]*>/, " ")
|> String.replace(" ", " ")
|> String.replace("&", "&")
|> String.replace("<", "<")
|> String.replace(">", ">")
|> String.replace(""", "\"")
|> String.replace("'", "'")
# Normalize whitespace
|> String.replace(~r/\s+/, " ")
|> String.trim()
end
end