123 lines
3.2 KiB
Elixir
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
|