Packages
phoenix_duskmoon
9.2.0
9.9.2
9.9.1
9.9.0
9.8.0
9.7.1
9.7.0
9.6.4
9.6.3
9.6.2
9.6.1
9.6.0
9.5.4
9.5.3
9.5.2
9.5.1
9.5.0
9.4.3
9.4.2
9.4.1
9.4.0
9.3.0
9.2.0
9.1.4
9.1.3
9.1.1
9.1.1-rc.1
retired
9.0.1
9.0.0-rc.3
9.0.0-rc.2
9.0.0-rc.1
8.0.0
7.2.1
7.2.0
7.1.3
7.1.2
7.1.1
6.3.2
6.3.1
6.3.0
6.2.0
6.1.3
6.1.2
6.1.1
6.1.0
6.0.10
6.0.9
6.0.8
6.0.7
6.0.6
6.0.5
6.0.4
6.0.3
6.0.2
6.0.1
6.0.0
5.2.0-beta.5
5.2.0-beta.4
5.2.0-beta.3
5.2.0-beta.2
5.2.0-beta.1
5.1.0
5.0.6
5.0.5
5.0.4
5.0.3
5.0.2
5.0.1
5.0.0
4.6.6
4.6.5
4.6.4
4.6.3
4.6.2
4.6.1
4.6.0
4.5.2
4.5.1
4.5.0
4.4.4
4.4.3
4.4.2
4.4.1
4.4.0
4.3.2
4.3.1
4.3.0
4.2.0
4.1.1
4.1.0
4.0.0
Duskmoon UI component library for Phoenix LiveView
Current section
Files
Jump to
Current section
Files
lib/phoenix_duskmoon/component/icon/icons.ex
defmodule PhoenixDuskmoon.Component.Icon.Icons do
@moduledoc """
Duskmoon UI Icons Component
Render 7000+ Material Design Icons with [mdi_icons]
Render 2000+ Bootstrap Icons with [bsi_icons]
"""
use PhoenixDuskmoon.Component, :html
# Load icon names at compile time. In dev (with SVG dirs present), read from
# priv/mdi/svg and priv/bsi/svg. In published Hex package (SVG dirs absent),
# fall back to the pre-bundled ETF files in priv/static/.
@md_icons (
svg_dir = Application.app_dir(:phoenix_duskmoon, "priv/mdi/svg")
bundle_path = Application.app_dir(:phoenix_duskmoon, "priv/static/mdi_icons.etf.gz")
cond do
File.dir?(svg_dir) ->
svg_dir
|> File.ls!()
|> Enum.filter(&String.ends_with?(&1, ".svg"))
|> Enum.map(&String.trim_trailing(&1, ".svg"))
|> Enum.sort(:asc)
File.exists?(bundle_path) ->
bundle_path
|> File.read!()
|> :zlib.gunzip()
|> :erlang.binary_to_term()
|> Map.keys()
|> Enum.sort(:asc)
true ->
[]
end
)
# Load icon bundle from compressed ETF, or fall back to individual SVG files.
# Bundles are generated by `mix icons.bundle` before hex publishing.
@doc false
defp load_icon_bundle(bundle_path, svg_dir) do
if File.exists?(bundle_path) do
bundle_path
|> File.read!()
|> :zlib.gunzip()
|> :erlang.binary_to_term()
else
svg_dir
|> File.ls!()
|> Enum.filter(&String.ends_with?(&1, ".svg"))
|> Map.new(fn filename ->
name = String.trim_trailing(filename, ".svg")
content = File.read!(Path.join(svg_dir, filename))
inner =
content
|> String.replace(~r/<svg[^>]+>/, "")
|> String.replace("</svg>", "")
{name, String.trim(inner)}
end)
end
end
@doc false
def get_mdi_svg(name) do
map =
try do
:persistent_term.get({__MODULE__, :mdi})
rescue
ArgumentError ->
app_dir = Application.app_dir(:phoenix_duskmoon)
loaded =
load_icon_bundle(
Path.join(app_dir, "priv/static/mdi_icons.etf.gz"),
Path.join(app_dir, "priv/mdi/svg")
)
:persistent_term.put({__MODULE__, :mdi}, loaded)
loaded
end
Map.get(map, name)
end
@doc false
def get_bsi_svg(name) do
map =
try do
:persistent_term.get({__MODULE__, :bsi})
rescue
ArgumentError ->
app_dir = Application.app_dir(:phoenix_duskmoon)
loaded =
load_icon_bundle(
Path.join(app_dir, "priv/static/bsi_icons.etf.gz"),
Path.join(app_dir, "priv/bsi/svg")
)
:persistent_term.put({__MODULE__, :bsi}, loaded)
loaded
end
Map.get(map, name)
end
@doc """
Return all names of available Material Design Icons (from `@mdi/svg` package).
Can be found at [Material Design Icons](https://duskmoon-storybook.gsmlg.dev/mdi)
> PhoenixDuskmoon.Component.Icon.Icons.mdi_icons()
#=> [
#=> "abacus",
#=> "abjad-arabic",
#=> ...
#=> ]
"""
@spec mdi_icons() :: [binary()]
def mdi_icons(), do: @md_icons
@doc """
Render Material Design Icons
## Examples
<.dm_mdi name="abacus" id="mdi-abjad-arabic" class="w-16 h-16" />
#=> <svg xmlns="http://www.w3.org/2000/svg" id="mdi-abjad-arabic" class="w-16 h-16" fill="currentcolor" viewBox="0 0 24 24"><path d="M12 4C10.08 4 8.5 5.58 8.5 7.5C8.5 8.43 8.88 9.28 9.5 9.91C7.97 10.91 7 12.62 7 14.5C7 17.53 9.47 20 12.5 20C14.26 20 16 19.54 17.5 18.66L16.5 16.93C15.28 17.63 13.9 18 12.5 18C10.56 18 9 16.45 9 14.5C9 12.91 10.06 11.53 11.59 11.12L16.8 9.72L16.28 7.79L11.83 9C11.08 8.9 10.5 8.28 10.5 7.5C10.5 6.66 11.16 6 12 6C12.26 6 12.5 6.07 12.75 6.2L13.75 4.47C13.22 4.16 12.61 4 12 4Z" /></svg>
"""
@doc type: :component
attr(:id, :any,
default: nil,
doc: """
html attribute id
"""
)
attr(:class, :any,
default: nil,
doc: """
html attribute class
"""
)
attr(:name, :string,
required: true,
doc: """
material icon name, avaliable names are return value of `mdi_icons()`.
"""
)
attr(:color, :string,
default: "currentcolor",
doc: """
icon color
"""
)
attr(:rest, :global, doc: "additional HTML attributes for the SVG element")
def dm_mdi(assigns) do
inner_svg =
case get_mdi_svg(assigns.name) do
nil ->
~s(<path d="M13,13H11V7H13M13,17H11V15H13M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2Z" />)
svg ->
svg
end
assigns = assigns |> assign(:inner_svg, inner_svg)
~H"""
<svg xmlns="http://www.w3.org/2000/svg" id={@id} class={@class} fill={@color} viewBox="0 0 24 24" aria-hidden="true" {@rest}>
{raw(@inner_svg)}
</svg>
"""
end
@bs_icons (
svg_dir = Application.app_dir(:phoenix_duskmoon, "priv/bsi/svg")
bundle_path = Application.app_dir(:phoenix_duskmoon, "priv/static/bsi_icons.etf.gz")
cond do
File.dir?(svg_dir) ->
svg_dir
|> File.ls!()
|> Enum.filter(&String.ends_with?(&1, ".svg"))
|> Enum.map(&String.trim_trailing(&1, ".svg"))
|> Enum.sort(:asc)
File.exists?(bundle_path) ->
bundle_path
|> File.read!()
|> :zlib.gunzip()
|> :erlang.binary_to_term()
|> Map.keys()
|> Enum.sort(:asc)
true ->
[]
end
)
@doc """
Return all names of available Bootstrap Icons (from `bootstrap-icons` package).
Can be found at [Bootstrap Icons](https://duskmoon-storybook.gsmlg.dev/bsi)
> PhoenixDuskmoon.Component.Icon.Icons.bsi_icons()
#=> [
"0-circle-fill",
"0-circle",
...
]
"""
@spec bsi_icons() :: [String.t()]
def bsi_icons(), do: @bs_icons
@doc """
Render Bootstrap Icons
## Examples
<.dm_bsi name="0-circle" class="w-16 h-16" />
#=> <svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" class="w-16 h-16" viewBox="0 0 16 16">
#=> <path d="M7.988 12.158c-1.851 0-2.941-1.57-2.941-3.99V7.84c0-2.408 1.101-3.996 2.965-3.996 1.857 0 2.935 1.57 2.935 3.996v.328c0 2.408-1.101 3.99-2.959 3.99ZM8 4.951c-1.008 0-1.629 1.09-1.629 2.895v.31c0 1.81.627 2.895 1.629 2.895s1.623-1.09 1.623-2.895v-.31c0-1.8-.621-2.895-1.623-2.895Z"/>
#=> <path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0ZM1 8a7 7 0 1 0 14 0A7 7 0 0 0 1 8Z"/>
#=> </svg>
"""
@doc type: :component
attr(:id, :any,
default: nil,
doc: """
html attribute id
"""
)
attr(:class, :any,
default: nil,
doc: """
html attribute class
"""
)
attr(:name, :string,
required: true,
doc: """
bootstrap icon name, avaliable names are return value of `bsi_icons()`.
"""
)
attr(:color, :string,
default: "currentcolor",
doc: """
icon color
"""
)
attr(:rest, :global, doc: "additional HTML attributes for the SVG element")
def dm_bsi(assigns) do
inner_svg =
case get_bsi_svg(assigns.name) do
nil ->
~s(<path d="M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14zm0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16z"/><path d="M7.002 11a1 1 0 1 1 2 0 1 1 0 0 1-2 0zM7.1 4.995a.905.905 0 1 1 1.8 0l-.35 3.507a.552.552 0 0 1-1.1 0L7.1 4.995z"/>)
svg ->
svg
end
assigns = assigns |> assign(:inner_svg, inner_svg)
~H"""
<svg xmlns="http://www.w3.org/2000/svg" id={@id} class={@class} fill={@color} viewBox="0 0 16 16" aria-hidden="true" {@rest}>
{raw(@inner_svg)}
</svg>
"""
end
end