dogweather

dogweather

First, the Hero icon infrastructure in Phoenix is fantastic — thank you for that.

I really like, though, getting as much help from my IDE as possible. And I try to minimize flipping between different screens for docs and translating in my head the third party naming conventions and how my app’s JS / CSS config names things.

So here, I’m brainstorming about ways to check my <.icon> uses at compile-time. There are 1,176 Hero icons, with all four variations.

Currently, I’m leaning towards generating a function for each so that I’d have nice function-name completion while typing. So:

Built-in

<.icon name="hero-plus-mini" />

Idea 1

<.icon_plus_mini />

There’d be 1,176 functions like this.

Idea 2

<.icon_plus kind = :mini />

There’d need to be only 294 functions.
Each would have an optional parameter with a locked-down @type kind :: :mini | :micro | :outline | :solid.

(I haven’t tested this syntax. And I’m thinking dialyzer would be needed to validate the kind argument.)

Idea 3

<.icon name = :plus, kind = :mini />

Have just 1 function that’d accept atoms for args, which would be used in structs, and therefore checked at compile-time.

I think the downside, though, is that the LSP / IDE wouldn’t be able to enumerate all the valid atom inputs in the same way it can enumerate valid function names. So while this would check at compile time, it wouldn’t be as convenient.


Has anyone thought about this?

Here’s an implementation of my first idea:

Summary
defmodule IndexFlowWeb.CoreComponents.IconGenerator do
  @moduledoc """
  Generates individual icon functions for each hero icon at compile time.
  """

  defmacro generate_icon_functions do
    icon_names = load_hero_icon_names()

    functions = for icon_name <- icon_names do
      # Convert "hero-plus-solid" to "icon_plus_solid"
      function_name =
        icon_name
        |> String.replace_prefix("hero-", "icon_")
        |> String.replace("-", "_")
        |> String.to_atom()

            quote do
        @doc """
        Renders the #{unquote(icon_name)} icon.

        ## Examples

            <.#{unquote(function_name)} />
            <.#{unquote(function_name)} class="h-4 w-4" />
        """
        def unquote(function_name)(assigns) do
          assigns = Map.put(assigns, :name, unquote(icon_name))
          icon(assigns)
        end
      end
    end

    {:__block__, [], functions}
  end

    defp load_hero_icon_names do
    # Use the deps directory path directly instead of Application.app_dir
    icons_dir = "deps/heroicons/optimized"

    # Define the mapping of directories to suffixes
    variants = [
      {"24/outline", ""},
      {"24/solid", "-solid"},
      {"20/solid", "-mini"},
      {"16/solid", "-micro"}
    ]

    # Use for comprehension to collect all icons
    for {dir, suffix} <- variants,
        full_path = Path.join(icons_dir, dir),
        File.exists?(full_path),
        file <- File.ls!(full_path),
        String.ends_with?(file, ".svg") do
      base_name = Path.basename(file, ".svg")
      "hero-#{base_name}#{suffix}"
    end
    |> Enum.uniq()
    |> Enum.sort()
  end
end

Showing Posts 1 to 3

cmo

cmo

You limit yourself to just heroicons in a few of those methods. I would add a brand or whatever attribute for future expansion.

I used to generate remix icons functions for the ones I used but migrated to the built-in method. Now I can use either set with the same component and syntax.

ouven

ouven

Hi, we use a semantic mapping in our icon component, like:

%{
faq: {:hero, „help“}
}

for instance.

At compile time we loop over our semantic names and build two functions for each. An icon component and a name function:

def icon_faq(assigns), do: …
def iconname_faq(), do: …

We still have a generic icon component to use inside other components with an icon attribute.

Now we use either the specific icon component or the name function for attributes.

Advantages are:

  • we can have different backends for different icons (hero and feather)
  • we have compile time checking
  • we have IDE support
  • we deliver only the icons we do actually use
  • semantic names are easy to remember

I hope you like some of the ideas.

rhcarvalho

rhcarvalho

If all you want is auto-complete, the Tailwind IntelliSense VSCode extension happily auto-completes the hero- classes for me.

All I had to do was update the config to tell it to auto-complete in an attribute named name (the default is class and className).

If you’re using another editor/IDE, a similar config might be available.

— All posts loaded —

Where Next? Top

Trending in Discussions Top

AstonJ
As the title says, please share what you’ve been up to with Elixir. Whether that’s been learning it, looking into it, making stuff with i...
2977 92995 915
New
caslu
I want to open this thread for you all to discuss and help those who really like Ash but are still hesitant to use it in a real project. ...
New
mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
Herve37
We’re evaluating API mocking tools for OpenAPI-based projects and would love to hear what other teams are using. We’re particularly inte...
New
GES233
I’m posting this in response to Jose’s recent tweet (Cr. link) : People are sleeping on Elixir for a coding harness: Hot-code swappi...
New
_mfierro
Hello, I wrote Stop My Hand, a Scattergories-like web application using Phoenix/LiveView as my learning project for Elixir (after readin...
New
nseaSeb
AcmeScript — Writing JS hooks as if I were still using Elixir I’ve been having fun building a little something over the last few days: Ac...
New

Other Trending Topics Top

garrison
Hobbes is a low-level distributed database for the Elixir programming language. Hobbes provides a simple, safe, and scalable storage lay...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
mcass19
ExRatatui lets you cook up rich terminal UIs in Elixir, powered by Rust’s ratatui via Rustler NIFs. Build interactive terminal applicatio...
New
Damirados
Hello everyone. After busy few months I am happy to announce v0.1.0 of Emerge &amp; Solve. They are GUI (Emerge) and State management (S...
New
netoum
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New
wintermeyer
There are three potential reasons for members of this forum to have a look at https://vutuv.de You are tired or annoyed of LinkedIn. Yo...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews