defmodule Tablerone do
# @related [tests](test/tablerone_test.exs)
@moduledoc """
Tablerone is a library for downloading specific tabler icons to the local application,
and then rendering them from the file system.
## Installation
Tablerone must be configured to know the parent application, in `config/config.exs`:
```elixir
import Config
# ...existing config
config :tablerone, :otp_app, <:my_app>
```
## Usage
When using new icons, they can be downloaded using the provided mix task:
```shell
mix tablerone.download <icon-name> <icon-name>
```
To load an icon in a heex component, the following example code may be used:
```elixir
attr :name, :atom, required: true
attr :class, :any, default: []
def icon(assigns) do
name = assigns[:name]
icon_contents = Tablerone.icon(name)
assigns =
assign_new(assigns, :icon_contents, fn ->
class = [class: assigns[:class]] |> Phoenix.HTML.attributes_escape() |> Phoenix.HTML.safe_to_string()
String.replace(icon_contents, ~r{class="[^"]+"}, class)
end)
~H\"""
<%= Phoenix.HTML.raw(@icon_contents) %>
\"""
end
```
"""
@doc """
Renders the given icon as a Phoenix component. If the icon has not been downloaded to the priv directory of the
parent application, `icon` will raise at run time, with instructions on how to run a mix task to download the icon.
## Examples
iex> icon = Tablerone.icon(:cactus_off, :outline)
iex> match?("<svg" <> _, icon)
true
"""
@spec icon(atom() | binary(), atom(), keyword()) :: binary()
def icon(name, type, opts \\ Application.get_all_env(:tablerone)) when type in ~w[filled outline]a do
name = dasherize(name)
svg_path = path(name, type, opts)
if File.exists?(svg_path) do
File.read!(svg_path)
else
raise ArgumentError, """
Icon `#{name}` has not been downloaded.
To download this icon to the local application, run the following in a terminal:
mix tablerone.download --type #{type} #{name}
"""
end
end
@doc """
Given an icon name as a string or atom, returns the expected path to the svg file.
The file is saved to a `tablerone` directory in the priv dir of the parent application,
specified by the `:tablerone, :otp_app` config.
"""
@spec path(atom() | binary(), atom(), keyword()) :: Path.t()
def path(icon_name, type, opts \\ Application.get_all_env(:tablerone)) do
otp_app = Keyword.fetch!(opts, :otp_app)
Path.join([:code.priv_dir(otp_app), "tablerone", "#{type}", "#{dasherize(icon_name)}.svg"])
end
@doc false
def dasherize(icon_name), do: icon_name |> to_string() |> String.replace("_", "-")
end