defmodule Heroicons do @moduledoc """ This package adds a convenient way of using [Heroicons](https://heroicons.com) with your Phoenix, Phoenix LiveView and Surface applications. Heroicons is "A set of 450+ free MIT-licensed high-quality SVG icons for you to use in your web projects." Created by the amazing folks at [Tailwind Labs](https://github.com/tailwindlabs). You can find the original docs [here](https://heroicons.com) and repo [here](https://github.com/tailwindlabs/heroicons). Current Heroicons version: 2.0.11 ## Installation Add `ex_heroicons` to the list of dependencies in `mix.exs`: def deps do [ {:ex_heroicons, "~> 2.0.0"} ] end Then run `mix deps.get`. ## Usage #### With Eex or Leex <%= Heroicons.icon("academic-cap", type: "outline", class: "h-4 w-4") %> #### With Heex #### With Surface ## Config Defaults can be set in the `Heroicons` application configuration. config :ex_heroicons, type: "outline" """ alias __MODULE__.Icon icon_paths = "node_modules/heroicons/**/*.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() @types types @names names @doc "Returns a list of available icon types" @spec types() :: [String.t()] def types(), do: @types @doc "Returns a list of available icon names" @spec names() :: [String.t()] def names(), do: @names @doc false def default_type() do case Application.get_env(:ex_heroicons, :type) do nil -> nil 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 end @doc """ Generates an icon. Options may be passed through to the SVG tag for custom attributes. ## Options * `:type` - the icon type. Accepted values are #{inspect(types)}. Required if default type is not configured. * `:class` - the css class added to the SVG tag ## Examples icon("academic-cap", type: "outline", class: "h-4 w-4") #=> """ @spec icon(String.t(), keyword) :: Phoenix.HTML.safe() def icon(name, opts \\ []) when is_binary(name) and is_list(opts) do {type, opts} = Keyword.pop(opts, :type, default_type()) unless type do raise ArgumentError, "expected type in options, got: #{inspect(opts)}" end unless type in types() do raise ArgumentError, "expected type to be one of #{inspect(types())}, got: #{inspect(type)}" end icon(type, name, opts) end for %Icon{type: type, name: name, file: file} <- icons do defp icon(unquote(type), unquote(name), opts) do attrs = Icon.opts_to_attrs(opts) Icon.insert_attrs(unquote(file), attrs) end end defp icon(type, name, _opts) do raise ArgumentError, "icon #{inspect(name)} with type #{inspect(type)} does not exist." end end