├── .formatter.exs
├── package.json
├── test
├── test_helper.exs
├── support
│ └── conn_case.ex
├── fontawesome_test.exs
├── surface
│ └── icon_test.exs
└── live_view_test.exs
├── .gitignore
├── LICENSE
├── lib
├── fontawesome
│ ├── live_view.ex
│ ├── icon.ex
│ └── surface
│ │ └── icon.ex
└── fontawesome.ex
├── README.md
├── .github
└── workflows
│ └── ci.yml
├── CHANGELOG.md
├── mix.exs
└── mix.lock
/.formatter.exs:
--------------------------------------------------------------------------------
1 | # Used by "mix format"
2 | [
3 | import_deps: [:surface],
4 | inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
5 | ]
6 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ex_fontawesome",
3 | "version": "0.7.2",
4 | "description": "ex_fontawesome",
5 | "main": "index.js",
6 | "directories": {
7 | "lib": "lib",
8 | "test": "test"
9 | },
10 | "scripts": {
11 | "test": "echo \"Error: no test specified\" && exit 1"
12 | },
13 | "author": "",
14 | "license": "ISC",
15 | "dependencies": {
16 | "@fortawesome/fontawesome-free": "^6.2.0"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/test/test_helper.exs:
--------------------------------------------------------------------------------
1 | ExUnit.start()
2 |
3 | defmodule Router do
4 | use Phoenix.Router
5 | end
6 |
7 | defmodule Endpoint do
8 | use Phoenix.Endpoint, otp_app: :surface
9 | plug(Router)
10 | end
11 |
12 | Application.put_env(:surface, Endpoint,
13 | secret_key_base: "J4lTFt000ENUVhu3dbIB2P2vRVl2nDBH6FLefnPUImL8mHYNX8Kln/N9J0HH19Mq",
14 | live_view: [
15 | signing_salt: "LfCCMxfkGME8S8P8XU3Z6/7+ZlD9611u"
16 | ]
17 | )
18 |
19 | Endpoint.start_link()
20 |
--------------------------------------------------------------------------------
/test/support/conn_case.ex:
--------------------------------------------------------------------------------
1 | defmodule FontAwesome.ConnCase do
2 | @moduledoc """
3 | This module defines the test case to be used by
4 | tests that require setting up a connection.
5 |
6 | It also imports other functionality to make it easier
7 | to test components.
8 | """
9 |
10 | use ExUnit.CaseTemplate
11 |
12 | using do
13 | quote do
14 | # Import conveniences for testing
15 | use Surface.LiveViewTest
16 |
17 | # The default endpoint for testing
18 | @endpoint Endpoint
19 | end
20 | end
21 |
22 | setup _tags do
23 | {:ok, conn: Phoenix.ConnTest.build_conn()}
24 | end
25 | end
26 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # The directory Mix will write compiled artifacts to.
2 | /_build/
3 |
4 | # If you run "mix test --cover", coverage assets end up here.
5 | /cover/
6 |
7 | # The directory Mix downloads your dependencies sources to.
8 | /deps/
9 |
10 | # Where third-party dependencies like ExDoc output generated docs.
11 | /doc/
12 |
13 | # Ignore .fetch files in case you like to edit your project deps locally.
14 | /.fetch
15 |
16 | # If the VM crashes, it generates a dump, let's ignore it too.
17 | erl_crash.dump
18 |
19 | # Also ignore archive artifacts (built via "mix archive.build").
20 | *.ez
21 |
22 | # Ignore package tarball (built via "mix hex.build").
23 | ex_fontawesome-*.tar
24 |
25 |
26 | # Temporary files for e.g. tests
27 | /tmp
28 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Miguel Serrano
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/lib/fontawesome/live_view.ex:
--------------------------------------------------------------------------------
1 | if Code.ensure_loaded?(Phoenix.LiveView) do
2 | defmodule FontAwesome.LiveView do
3 | @moduledoc """
4 | A LiveView component for rendering Font Awesome icons.
5 |
6 | ## Examples
7 |
8 |
9 | """
10 |
11 | use Phoenix.Component
12 |
13 | def icon(assigns) do
14 | opts = assigns[:opts] || []
15 | type_opts = type_to_opts(assigns)
16 | class_opts = class_to_opts(assigns)
17 |
18 | opts =
19 | opts
20 | |> Keyword.merge(type_opts)
21 | |> Keyword.merge(class_opts)
22 |
23 | assigns = assign(assigns, opts: opts)
24 |
25 | ~H"""
26 | <%= FontAwesome.icon(@name, @opts) %>
27 | """
28 | end
29 |
30 | defp type_to_opts(assigns) do
31 | type = assigns[:type] || FontAwesome.default_type()
32 |
33 | unless type do
34 | raise ArgumentError,
35 | "type prop is required if default type is not configured."
36 | end
37 |
38 | [type: type]
39 | end
40 |
41 | defp class_to_opts(assigns) do
42 | if assigns[:class] do
43 | [class: assigns.class]
44 | else
45 | []
46 | end
47 | end
48 | end
49 | end
50 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # FontAwesome
2 |
3 | 
4 |
5 | This package adds a convenient way of using [Font Awesome](https://fontawesome.com) SVGs with your Phoenix, Phoenix LiveView and Surface applications.
6 |
7 | You can find the original docs [here](https://fontawesome.com) and repo [here](https://github.com/FortAwesome/Font-Awesome).
8 |
9 | Current FontAwesome version: 6.2.0
10 |
11 | ## Installation
12 |
13 | Add `ex_fontawesome` to the list of dependencies in `mix.exs`:
14 |
15 | def deps do
16 | [
17 | {:ex_fontawesome, "~> 0.7.2"}
18 | ]
19 | end
20 |
21 | Then run `mix deps.get`.
22 |
23 | ## Usage
24 |
25 | #### With Eex or Leex
26 |
27 | ```elixir
28 | <%= FontAwesome.icon("address-book", type: "regular", class: "h-4 w-4") %>
29 | ```
30 |
31 | #### With Heex
32 |
33 | ```elixir
34 |
35 | ```
36 |
37 | #### With Surface
38 |
39 | ```elixir
40 |
41 | ```
42 |
43 | ## Config
44 |
45 | Defaults can be set in the `FontAwesome` application configuration.
46 |
47 | ```elixir
48 | config :ex_fontawesome, type: "regular"
49 | ```
50 |
51 | ## License
52 |
53 | MIT. See [LICENSE](https://github.com/miguel-s/ex_fontawesome/blob/master/LICENSE) for more details.
54 |
--------------------------------------------------------------------------------
/lib/fontawesome/icon.ex:
--------------------------------------------------------------------------------
1 | defmodule FontAwesome.Icon do
2 | @moduledoc """
3 | This module defines the data structure and functions for working with icons stored as SVG files.
4 | """
5 |
6 | alias __MODULE__
7 |
8 | @doc """
9 | Defines the FontAwesome.Icon struct.
10 |
11 | Its fields are:
12 |
13 | * `:type` - the type of the icon
14 |
15 | * `:name` - the name of the icon
16 |
17 | * `:file` - the binary of the icon
18 |
19 | """
20 | defstruct [:type, :name, :file]
21 |
22 | @type t :: %Icon{type: String.t(), name: String.t(), file: binary}
23 |
24 | @doc "Parses a SVG file and returns structured data"
25 | @spec parse!(String.t()) :: Icon.t()
26 | def parse!(filename) do
27 | [type, name] = filename |> Path.split() |> Enum.take(-2)
28 | name = Path.rootname(name)
29 | file = File.read!(filename)
30 | struct!(__MODULE__, type: type, name: name, file: file)
31 | end
32 |
33 | @doc "Converts opts to HTML attributes"
34 | @spec opts_to_attrs(keyword) :: list
35 | def opts_to_attrs(opts) do
36 | for {key, value} <- opts do
37 | key =
38 | key
39 | |> Atom.to_string()
40 | |> String.replace("_", "-")
41 | |> Phoenix.HTML.Safe.to_iodata()
42 |
43 | value = Phoenix.HTML.Safe.to_iodata(value)
44 |
45 | [?\s, key, ?=, ?", value, ?"]
46 | end
47 | end
48 |
49 | @doc "Inserts HTML attributes into an SVG icon"
50 | @spec insert_attrs(binary, keyword) :: Phoenix.HTML.safe()
51 | def insert_attrs("