defmodule FrancisHtmx do @moduledoc """ Provides a macro to render htmx content by loading htmx.js. Also provides a sigil to render EEx content similar to ~H from Phoenix.LiveView Usage: ```elixir defmodule Example do use Francis import FrancisHtmx htmx(fn _conn -> assigns = %{} ~E\"\"\"

Color Swap Demo

\"\"\" end) get("/colors", fn _ -> new_color = 3 |> :crypto.strong_rand_bytes() |> Base.encode16() |> then(&"\#{&1}") assigns = %{new_color: new_color} ~E\"\"\"

"> Color Swap Demo

\"\"\" end) end ``` In this scenario we are loading serving an HTML that has the htmx.js library loaded and serves the root content given by htmx/1 """ @doc """ Renders htmx content by loading htmx.js and rendering binary content. """ @spec htmx((Plug.Conn.t() -> binary()), Keyword.t()) :: Macro.t() defmacro htmx(content, opts \\ []) do title = Keyword.get(opts, :title, "") head = Keyword.get(opts, :head, "") quote location: :keep do get("/", fn conn -> """ #{unquote(head)} #{unquote(title)} #{unquote(content).(conn)} """ end) end end @doc """ Provides a sigil to render EEx content similar to ~H from Phoenix.LiveView Requires a variable named "assigns" to exist and be set to a map. """ @spec sigil_E(String.t(), Keyword.t()) :: Macro.t() defmacro sigil_E(content, _opts \\ []) do unless Macro.Env.has_var?(__CALLER__, {:assigns, nil}) do raise "~E requires a variable named \"assigns\" to exist and be set to a map" end quote location: :keep do content = EEx.eval_string(unquote(content), [assigns: var!(assigns)], engine: Phoenix.HTML.Engine) content |> Phoenix.HTML.html_escape() |> Phoenix.HTML.safe_to_string() end end end