Skip to main content

src/gg_icon@icon.erl

-module(gg_icon@icon).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/gg_icon/icon.gleam").
-export([size/1, svg/4, fallback_box/1, placeholder/3]).
-export_type([size/0]).

-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.

?MODULEDOC(
    " gg_icon — the set-agnostic icon interface every `gg_icons_*` package builds\n"
    " on. It holds the size scale, the shared `svg` wrapper (generic over `viewBox`\n"
    " + per-variant defaults, so it serves stroke *and* fill variants), and the\n"
    " authoring `placeholder`. It never imports a concrete icon set and ships no\n"
    " Tailwind — keeping it a lean, dual-target foundation.\n"
    "\n"
    " See `dev-docs/icons.md` in the gg_ui repo for the full design.\n"
).

-type size() :: sm | md | lg.

-file("src/gg_icon/icon.gleam", 33).
-spec size_class(size()) -> binary().
size_class(Size) ->
    case Size of
        sm ->
            <<"cn-icon-size-sm"/utf8>>;

        md ->
            <<"cn-icon-size-md"/utf8>>;

        lg ->
            <<"cn-icon-size-lg"/utf8>>
    end.

-file("src/gg_icon/icon.gleam", 29).
?DOC(
    " A class attribute selecting a named size. Pass it in an icon's `attrs`:\n"
    " `lucide.star([icon.size(icon.Lg)])`. For a one-off, pass a raw\n"
    " `attribute.class(\"size-[18px]\")` instead — it wins by source order.\n"
).
-spec size(size()) -> lustre@vdom@vattr:attribute(any()).
size(Size) ->
    lustre@attribute:class(size_class(Size)).

-file("src/gg_icon/icon.gleam", 49).
?DOC(
    " Build an icon `<svg>`. Generated icon functions call this with their\n"
    " variant's `view_box` + `defaults` (e.g. `fill=none stroke=currentColor` for\n"
    " an outline variant, `fill=currentColor` for a filled one). `attrs` is\n"
    " appended **last** so caller overrides win by source order. Everything stays\n"
    " `currentColor`, so `color` / `text-*` drive the colour and `size` /\n"
    " `size-*` drive the size.\n"
).
-spec svg(
    binary(),
    list({binary(), binary()}),
    list(lustre@vdom@vattr:attribute(PBL)),
    list(lustre@vdom@vnode:element(PBL))
) -> lustre@vdom@vnode:element(PBL).
svg(View_box, Defaults, Attrs, Children) ->
    lustre@element@svg:svg(
        lists:append(
            [[lustre@attribute:class(<<"cn-icon"/utf8>>),
                    lustre@attribute:attribute(<<"viewBox"/utf8>>, View_box),
                    lustre@attribute:attribute(
                        <<"data-slot"/utf8>>,
                        <<"icon"/utf8>>
                    ),
                    lustre@attribute:attribute(
                        <<"aria-hidden"/utf8>>,
                        <<"true"/utf8>>
                    )],
                gleam@list:map(
                    Defaults,
                    fun(D) ->
                        lustre@attribute:attribute(
                            erlang:element(1, D),
                            erlang:element(2, D)
                        )
                    end
                ),
                Attrs]
        ),
        Children
    ).

-file("src/gg_icon/icon.gleam", 90).
?DOC(
    " A neutral rounded square — the placeholder's no-resolver fallback, and a\n"
    " visible \"icon would go here\" marker in un-transformed previews.\n"
).
-spec fallback_box(list(lustre@vdom@vattr:attribute(PBV))) -> lustre@vdom@vnode:element(PBV).
fallback_box(Attrs) ->
    svg(
        <<"0 0 24 24"/utf8>>,
        [{<<"fill"/utf8>>, <<"none"/utf8>>},
            {<<"stroke"/utf8>>, <<"currentColor"/utf8>>},
            {<<"stroke-width"/utf8>>, <<"2"/utf8>>},
            {<<"stroke-linecap"/utf8>>, <<"round"/utf8>>},
            {<<"stroke-linejoin"/utf8>>, <<"round"/utf8>>}],
        Attrs,
        [lustre@element@svg:path(
                [lustre@attribute:attribute(
                        <<"d"/utf8>>,
                        <<"M4 4h16v16H4z"/utf8>>
                    )]
            )]
    ).

-file("src/gg_icon/icon.gleam", 80).
?DOC(
    " Authoring placeholder used in registry source. The (future) CLI transformer\n"
    " replaces each call with a direct `<shard>.<icon>(attrs)` for the user's\n"
    " chosen set/variant, so this never reaches a shipped bundle. Until then — and\n"
    " in any non-transformed runtime — it renders a neutral fallback box.\n"
    "\n"
    " Storybook's live preview will inject a resolver here so embedded icons\n"
    " (a dialog-close `x`) switch with the toolbar; that wiring lands with the\n"
    " `apps/storybook` demo catalog and keeps `gg_icon` set-agnostic.\n"
).
-spec placeholder(binary(), binary(), list(lustre@vdom@vattr:attribute(PBR))) -> lustre@vdom@vnode:element(PBR).
placeholder(_, _, Attrs) ->
    fallback_box(Attrs).