123 lines
3.2 KiB
Elixir

defmodule BrightWeb.SVGIcon do
@moduledoc """
This package adds a convenient way of using svg icons with your Phoenix, Phoenix LiveView and Surface applications.
greets https://github.com/miguel-s/ex_heroicons/blob/main/lib/heroicons.ex
## Usage
<SVGIcons.icon name="chaturbate" class="h-4 w-4" />
## Config
Defaults can be set in the application configuration.
config :bright, :icons_type: "outline"
"""
use Phoenix.Component
alias BrightWeb.SVGIcon.Icon
svg_icons_path = "priv/static/assets/icons"
unless File.exists?(svg_icons_path) do
raise """
SVG icons not found. Expected to load them from #{svg_icons_path}.
"""
end
icon_paths =
svg_icons_path
|> Path.join("**/*.svg")
|> Path.wildcard()
icons =
for icon_path <- icon_paths do
@external_resource Path.relative_to_cwd(icon_path)
Icon.parse!(icon_path)
end
types = icons |> Enum.map(& &1.type) |> Enum.uniq()
names = icons |> Enum.map(& &1.name) |> Enum.uniq()
default_type =
case Application.compile_env(:bright, :icons_type) do
nil ->
"solid"
type when is_binary(type) ->
if type in types do
type
else
raise ArgumentError,
"expected default type to be one of #{inspect(types)}, got: #{inspect(type)}"
end
type ->
raise ArgumentError,
"expected default type to be one of #{inspect(types)}, got: #{inspect(type)}"
end
@names names
def names, do: @names
@types types
def types, do: @types
attr :name, :string, values: @names, required: true, doc: "the name of the icon"
attr :type, :string, values: @types, default: default_type, doc: "the type of the icon"
attr :class, :string, default: nil, doc: "the css classes to add to the svg container"
attr :rest, :global, doc: "the arbitrary HTML attributes to add to the svg container"
def icon(assigns) do
name = assigns[:name]
if name == nil or name not in @names do
raise ArgumentError,
"expected icon name to be one of #{inspect(unquote(@names))}, got: #{inspect(name)}"
end
type = assigns[:type]
if type == nil or type not in @types do
raise ArgumentError,
"expected icon type to be one of #{inspect(unquote(@types))}, got: #{inspect(type)}"
end
~H"""
<span class="icon">
<.svg_container focusable="false" type={@type} class={@class} {@rest}>
<%= {:safe, svg_body(@name, @type)} %>
</.svg_container>
</span>
"""
end
attr :type, :string, values: @types, default: default_type, doc: "the type of the icon"
attr :class, :string, default: nil, doc: "the css classes to add to the svg container"
attr :rest, :global, doc: "the arbitrary HTML attributes to add to the svg container"
slot :inner_block, required: true, doc: "the svg to render"
defp svg_container(assigns) do
~H"""
<%= render_slot(@inner_block) %>
"""
end
defp svg_viewbox(type) do
case type do
"micro" -> "0 0 16 16"
"mini" -> "0 0 20 20"
"solid" -> "0 0 24 24"
"outline" -> "0 0 24 24"
end
end
for %Icon{name: name, type: type, file: file} <- icons do
defp svg_body(unquote(name), unquote(type)) do
unquote(file)
end
end
end