defmodule ExIcon do @moduledoc """ Refer to the readme for usage instructions. This module only contains helper functions that you probably don't need to use directly. """ @default_config_path ".ex_icon.exs" @options_schema [ icons: [ type: {:or, [{:list, :string}, {:in, [:all]}]}, required: true, doc: """ Either a list of icon names you want to generate (e.g. `["arrow-left"]`), or `:all` if you want to generate all available icons. """ ], provider: [ type: :atom, required: true, doc: "A module implementing the `ExIcon.Provider` behaviour." ], version: [ type: :string, required: true, doc: "The release version of the icon library." ], module_path: [ type: :string, required: true, doc: """ The destination path of the icon module that ExIcon will generate for you. Example: `"lib/my_app_web/components/lucide.ex"`. """ ], module_name: [ type: :atom, required: true, doc: "The name of the generated module. Example: `MyApp.Components.Lucide`." ], attrs: [ type: {:list, :string}, required: false, default: [], doc: """ ExIcon substitutes all listed attributes of the `` element with HEEx variables and adds the corresponding attributes to the HEEx components. """ ] ] @config_schema NimbleOptions.new!( *: [type: :keyword_list, keys: @options_schema] ) @typedoc """ #{NimbleOptions.docs(@options_schema)} """ @type options() :: [unquote(NimbleOptions.option_typespec(@options_schema))] @doc """ Takes an SVG as a string, extracts the attributes, and replaces the attributes with HEEx variables. The second argument is a list of attributes to turn into component attributes. It must be a list of lowercase strings. Attributes not present in the original SVG file are ignored. The function returns a tuple with the updated SVG string as the first element and a list of substituted attributes with their values as a second element. The attribute name is converted to snake case. An `aria-hidden="true"` attribute is added if not already present. ## Example iex> svg = \"\"\" ...> xmlns="http://www.w3.org/2000/svg" ...> width="24" ...> height="24" ...> viewBox="0 0 24 24" ...> stroke="currentColor" ...> stroke-width="2" ...> > ...> ...> ...> ...> \"\"\" iex> ExIcon.transform_svg(svg) {\"\"\" \\ \"\"\", []} iex> svg = \"\"\" ...> xmlns="http://www.w3.org/2000/svg" ...> width="24" ...> height="24" ...> viewBox="0 0 24 24" ...> stroke="currentColor" ...> stroke-width="2" ...> > ...> ...> ...> ...> \"\"\" iex> ExIcon.transform_svg(svg, ["stroke", "stroke-width"]) {\"\"\" \\ \"\"\", [{"stroke", "currentColor"}, {"stroke_width", "2"}]} """ @spec transform_svg(svg, attrs) :: {svg, attrs} when svg: binary, attrs: [binary], attrs: [{binary, binary}] def transform_svg(svg, substitute_attrs \\ []) when is_binary(svg) and is_list(substitute_attrs) do case extract_svg(svg) do {:ok, {attrs, inner}} -> svg_attrs = build_attrs(attrs, substitute_attrs) substituted_attrs = attrs |> Enum.filter(fn {k, _} -> substitute_attr?(k, substitute_attrs) end) |> Enum.map(fn {k, v} -> {to_snake_case(k), v} end) svg = ~s() {svg, substituted_attrs} :error -> # original SVG has no attributes svg = svg |> String.trim() |> String.replace("", ~s(