├── test ├── test_helper.exs └── heroicons_test.exs ├── .formatter.exs ├── .gitignore ├── .github └── workflows │ └── ci.yml ├── LICENSE ├── lib ├── heroicons │ └── icon.ex └── heroicons.ex ├── README.md ├── mix.exs ├── CHANGELOG.md └── mix.lock /test/test_helper.exs: -------------------------------------------------------------------------------- 1 | ExUnit.start() 2 | -------------------------------------------------------------------------------- /.formatter.exs: -------------------------------------------------------------------------------- 1 | # Used by "mix format" 2 | [ 3 | import_deps: [:phoenix], 4 | plugins: [Phoenix.LiveView.HTMLFormatter], 5 | inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{heex,ex,exs}"] 6 | ] 7 | -------------------------------------------------------------------------------- /.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 3rd-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 | # Temporary files, for example, from tests. 23 | /tmp/ 24 | 25 | # Ignore package tarball (built via "mix hex.build"). 26 | ex_heroicons-*.tar 27 | 28 | # Ignore assets that are produced by build tools. 29 | /priv/static/assets/ 30 | 31 | # Ignore digested assets cache. 32 | /priv/static/cache_manifest.json 33 | 34 | # In case you use Node.js/npm, you want to ignore these. 35 | npm-debug.log 36 | /assets/node_modules/ 37 | 38 | # Ignore elixir-ls cache 39 | /.elixir_ls/ 40 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: 7 | - main 8 | 9 | jobs: 10 | mix_test: 11 | name: mix test (Elixir ${{matrix.elixir}} | Erlang/OTP ${{matrix.otp}}) 12 | runs-on: ubuntu-latest 13 | strategy: 14 | fail-fast: false 15 | matrix: 16 | include: 17 | - elixir: 1.14.0 18 | otp: 25.0 19 | lint: true 20 | env: 21 | MIX_ENV: test 22 | steps: 23 | - uses: actions/checkout@v2.3.2 24 | - uses: erlef/setup-beam@v1 25 | with: 26 | otp-version: ${{matrix.otp}} 27 | elixir-version: ${{matrix.elixir}} 28 | - name: Install packages 29 | run: | 30 | sudo apt-get install -y -qq inotify-tools 31 | - name: Install Dependencies 32 | run: | 33 | mix local.hex --force 34 | mix local.rebar --force 35 | mix deps.get --only test 36 | - run: mix format --check-formatted 37 | if: matrix.lint 38 | - run: mix compile --warnings-as-errors 39 | if: matrix.lint 40 | - run: mix test 41 | -------------------------------------------------------------------------------- /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/heroicons/icon.ex: -------------------------------------------------------------------------------- 1 | defmodule Heroicons.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 Heroicons.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 content of the file 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] = 28 | filename 29 | |> Path.split() 30 | |> Enum.take(-3) 31 | |> case do 32 | ["16", "solid", name] -> ["micro", name] 33 | ["20", "solid", name] -> ["mini", name] 34 | ["24", "solid", name] -> ["solid", name] 35 | ["24", "outline", name] -> ["outline", name] 36 | end 37 | 38 | name = Path.rootname(name) 39 | 40 | file = 41 | filename 42 | |> File.read!() 43 | |> String.split("\n") 44 | |> Enum.drop(1) 45 | |> Enum.drop(-2) 46 | |> Enum.map(&String.trim/1) 47 | 48 | %__MODULE__{type: type, name: name, file: file} 49 | end 50 | end 51 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Heroicons 2 | 3 | ![CI](https://github.com/miguel-s/ex_heroicons/actions/workflows/ci.yml/badge.svg) 4 | 5 | This package adds a convenient way of using [Heroicons](https://heroicons.com) with your Phoenix, Phoenix LiveView and Surface applications. 6 | 7 | Heroicons is "A set of 450+ free MIT-licensed high-quality SVG icons for you to use in your web projects." 8 | Created by the amazing folks at [Tailwind Labs](https://github.com/tailwindlabs). 9 | 10 | You can find the original docs [here](https://heroicons.com) and repo [here](https://github.com/tailwindlabs/heroicons). 11 | 12 | ## Installation 13 | 14 | Add `ex_heroicons` and `heroicons` to the list of dependencies in `mix.exs`: 15 | 16 | ```elixir 17 | def deps do 18 | [ 19 | {:ex_heroicons, "~> 3.1.0"}, 20 | {:heroicons, 21 | github: "tailwindlabs/heroicons", 22 | tag: "v2.1.5", 23 | sparse: "optimized", 24 | app: false, 25 | compile: false, 26 | depth: 1} 27 | ] 28 | end 29 | ``` 30 | 31 | Then run `mix deps.get`. 32 | 33 | ## Usage 34 | 35 | ```elixir 36 | 37 | ``` 38 | 39 | ## Config 40 | 41 | Defaults can be set in the `Heroicons` application configuration. 42 | 43 | ```elixir 44 | config :ex_heroicons, type: "outline" 45 | ``` 46 | 47 | ## License 48 | 49 | MIT. See [LICENSE](https://github.com/miguel-s/ex_heroicons/blob/master/LICENSE) for more details. 50 | -------------------------------------------------------------------------------- /mix.exs: -------------------------------------------------------------------------------- 1 | defmodule Heroicons.MixProject do 2 | use Mix.Project 3 | 4 | @version "3.1.0" 5 | 6 | def project do 7 | [ 8 | app: :ex_heroicons, 9 | version: @version, 10 | elixir: "~> 1.14", 11 | elixirc_paths: elixirc_paths(Mix.env()), 12 | start_permanent: Mix.env() == :prod, 13 | deps: deps(), 14 | docs: docs(), 15 | description: description(), 16 | package: package(), 17 | source_url: "https://github.com/miguel-s/ex_heroicons" 18 | ] 19 | end 20 | 21 | def application do 22 | [ 23 | extra_applications: [:logger] 24 | ] 25 | end 26 | 27 | defp elixirc_paths(:test), do: ["lib", "test/support"] 28 | defp elixirc_paths(_), do: ["lib"] 29 | 30 | defp deps do 31 | [ 32 | {:phoenix_live_view, "~> 0.20 or ~> 1.0"}, 33 | {:ex_doc, "~> 0.35", only: :dev, runtime: false}, 34 | {:floki, ">= 0.36.0", only: :test}, 35 | {:heroicons, 36 | github: "tailwindlabs/heroicons", 37 | tag: "v2.1.5", 38 | sparse: "optimized", 39 | app: false, 40 | compile: false, 41 | depth: 1, 42 | only: :test} 43 | ] 44 | end 45 | 46 | defp docs do 47 | [ 48 | main: "Heroicons", 49 | source_ref: "v#{@version}", 50 | source_url: "https://github.com/miguel-s/ex_heroicons", 51 | extras: ["README.md"] 52 | ] 53 | end 54 | 55 | defp description() do 56 | """ 57 | This package adds a convenient way of using Heroicons with your Phoenix, Phoenix LiveView and Surface applications. 58 | 59 | Heroicons is "A set of 450+ free MIT-licensed high-quality SVG icons for you to use in your web projects." 60 | Created by the amazing folks at Tailwind Labs. 61 | """ 62 | end 63 | 64 | defp package do 65 | %{ 66 | files: ~w(lib .formatter.exs mix.exs README* LICENSE* CHANGELOG*), 67 | licenses: ["MIT"], 68 | links: %{"GitHub" => "https://github.com/miguel-s/ex_heroicons"} 69 | } 70 | end 71 | end 72 | -------------------------------------------------------------------------------- /test/heroicons_test.exs: -------------------------------------------------------------------------------- 1 | defmodule HeroiconsTest do 2 | use ExUnit.Case, async: true 3 | import Phoenix.Component 4 | import Phoenix.LiveViewTest 5 | doctest Heroicons 6 | 7 | test "renders icon" do 8 | assigns = %{} 9 | html = rendered_to_string(~H[]) 10 | 11 | assert html =~ 12 | ~s(\n) 13 | end 14 | 15 | test "renders icon with default type" do 16 | assigns = %{} 17 | html = rendered_to_string(~H[]) 18 | assert html =~ ~s(heroicons-outline-academic-cap) 19 | end 20 | 21 | test "renders icon with explicit type" do 22 | assigns = %{} 23 | html = rendered_to_string(~H[]) 24 | assert html =~ ~s(heroicons-solid-academic-cap) 25 | end 26 | 27 | test "renders icon with class" do 28 | assigns = %{} 29 | html = rendered_to_string(~H[]) 30 | assert html =~ ~s(class="h-4 w-4") 31 | end 32 | 33 | test "renders icon with global attributes" do 34 | assigns = %{} 35 | html = rendered_to_string(~H[