MishkaCmsCore.Runtime.PageBuilder.Icons.Heroicons (Mishka CMS Core v0.0.1)

Copy Markdown View Source

The heroicon set, as a MishkaCmsCore.Runtime.PageBuilder.IconLibrary.

Why this exists

An icon attribute's value is a class name — hero-check, hero-check-solid — resolved by the Tailwind heroicons plugin at priv/static/temp/js_assets/vendor/heroicons.js, which reads the SVGs out of deps/heroicons/optimized and emits one hero-* component class per file. Nothing in the kit bundle carries that list: Chelekom's icon component declares attr :name, :string with no values and no docs, so the editor's only options were to guess or to make the author type hero-exclamation-triangle from memory, with a typo producing a silently missing glyph.

So the list is read from the same directory the plugin reads, at compile time: the heroicons dependency in the deps of the project being built (Mix.Project.deps_path/0) — this library's own when it is built alone, its host's when it is a dependency. Compile time is the point: deps/ is a build-time artefact and a release does not ship it, while the class names the release renders are baked into its CSS — reading at runtime would work in dev and return an empty picker in production.

The four variants

The plugin's own table, which this mirrors exactly:

""       → 24/outline    (the default weight)
"-solid" → 24/solid
"-mini"  → 20/solid
"-micro" → 16/solid

A name is offered in every variant the set actually ships it in — 16/solid carries eight fewer icons than the others, so a blanket suffix list would advertise glyphs that render as nothing.

When the catalogue is empty

If the directory cannot be found the module compiles to an empty catalogue rather than failing the build, and available?/0 answers false for this set: every variant lists no names, svg/2 is nil and css/1 is "". The picker then falls back to a plain text box — a host that does not use heroicons loses the picker, not the editor.

Summary

Functions

Every icon class the set ships, sorted — hero-academic-cap, hero-academic-cap-micro, …

Whether a catalogue was found at compile time. False means the picker degrades to a text box.

How many icons the query matches in a variant, for the picker's counter.

CSS rules for the named icon classes, mirroring what the Tailwind heroicons plugin emits.

Whether a value names an icon this set actually ships.

The bare names in one variant, without the hero- prefix or the suffix.

The icons matching a query, capped.

One icon as inline SVG, sized to size pixels — nil when the set does not ship it.

The variant suffixes, in the plugin's own order — outline first, because it is the default.

Functions

all()

@spec all() :: [String.t()]

Every icon class the set ships, sorted — hero-academic-cap, hero-academic-cap-micro, …

available?()

@spec available?() :: boolean()

Whether a catalogue was found at compile time. False means the picker degrades to a text box.

count(query, variant \\ "")

@spec count(String.t() | nil, String.t()) :: non_neg_integer()

How many icons the query matches in a variant, for the picker's counter.

Counted rather than length(search(...)) because the search is capped — the count is what tells a reader their query is too broad, and a capped list cannot say so.

css(names)

@spec css([String.t()]) :: String.t()

CSS rules for the named icon classes, mirroring what the Tailwind heroicons plugin emits.

The editor cannot rely on the site's compiled stylesheet for these. That stylesheet is generated from the classes the site's own pages RENDER, and a component's default icon appears in no page's HTML — so chelekom-alert's hero-chat-bubble-bottom-center-text had a 28×28 box on the canvas with mask-image: none inside it, which reads as "the icon is broken" rather than "this class was never compiled".

The rule is the plugin's own, restated: the SVG as a data URI mask, currentColor as the paint, and the plugin's size table (24px, or 20px for -mini, 16px for -micro). Only the classes actually in front of the reader are emitted — a few per page, not all 1,288.

known?(name)

@spec known?(String.t() | nil) :: boolean()

Whether a value names an icon this set actually ships.

names(suffix \\ "")

@spec names(String.t()) :: [String.t()]

The bare names in one variant, without the hero- prefix or the suffix.

search(query, opts \\ [])

@spec search(String.t() | nil, keyword()) :: [String.t()]

The icons matching a query, capped.

The query is matched against the name with its hero- prefix and variant suffix stripped, so "arrow" finds hero-arrow-up and hero-arrow-up-solid alike, and a reader who types the whole class name still finds it. An empty query returns the head of the list rather than nothing: an empty picker looks broken, and the first page of icons is a reasonable place to start browsing.

svg(name, size \\ 15)

@spec svg(String.t(), pos_integer()) :: {:safe, iodata()} | nil

One icon as inline SVG, sized to size pixels — nil when the set does not ship it.

The source file's own <svg> attributes are kept (viewBox, fill, stroke-width), because a 20px solid icon and a 24px outline icon are drawn differently and a single hardcoded viewBox would squash half the set.

variants()

@spec variants() :: [{String.t(), String.t()}]

The variant suffixes, in the plugin's own order — outline first, because it is the default.