# `Hypa.Plug`
[🔗](https://code.cosmicrose.dev/rosa/hypa)

A `Plug` offering functions useful for applications using HTMX.

This plug invokes the following function plugs:

- `detect_htmx_request/2` - Adds a `:htmx` map to `conn.assigns` containing htmx request headers.
- `add_htmx_cache_control/2` - Adds `hx-request` to the response's `vary` header

The following functions are also invoked if `Phoenix.Controller` is present at compile-time:

- `put_htmx_layout/2` - Disables the root and app layouts if responding to an htmx request

# `add_htmx_cache_control`
*since 0.2.0* 

```elixir
@spec add_htmx_cache_control(Plug.Conn.t(), Plug.opts()) :: Plug.Conn.t()
```

Adds htmx to the response's cache headers in order for caching to work correctly.

## Examples

    iex> conn(:get, "/hello")
    ...> |> Hypa.Plug.add_htmx_cache_control(Hypa.Plug.init([]))
    ...> |> get_resp_header("vary")
    ["hx-request"]

# `call`
*since 0.1.0* 

# `detect_htmx_request`
*since 0.1.0* 

```elixir
@spec detect_htmx_request(Plug.Conn.t(), Plug.opts()) :: Plug.Conn.t()
```

Extracts HTMX headers into the conn's assigns.

See [Request Headers Reference] for documentation on htmx's header values.

[Request Headers Reference]: https://htmx.org/reference/#request_headers

## Examples

    iex> conn(:get, "/hello")
    ...> |> merge_req_headers([
    ...>      {"hx-request", "true"},
    ...>      {"hx-current-url", "/current"},
    ...>      {"hx-history-restore-request", "false"},
    ...>      {"hx-target", "my-target-element-id"},
    ...>      {"hx-trigger-name", "my-triggered-element-name"},
    ...>      {"hx-trigger", "my-triggered-element-id"}
    ...>    ])
    ...> |> Hypa.Plug.detect_htmx_request(Hypa.Plug.init([]))
    ...> |> Map.get(:assigns)
    ...> |> Map.get(:htmx)
    %{
      request: true,
      boosted: false,
      current_url: "/current",
      history_restore_request: false,
      prompt: nil,
      target: "my-target-element-id",
      trigger: "my-triggered-element-id",
      trigger_name: "my-triggered-element-name"
    }

If the request does not have htmx headers, the `:htmx` assign will not be present.

    iex> conn(:get, "/htmxless")
    ...> |> Hypa.Plug.detect_htmx_request(Hypa.Plug.init([]))
    ...> |> Map.get(:assigns)
    ...> |> Map.has_key?(:htmx)
    false

# `init`
*since 0.1.0* 

# `put_htmx_layout`
*since 0.2.0* 

```elixir
@spec put_htmx_layout(Plug.Conn.t(), Plug.opts()) :: Plug.Conn.t()
```

Disable Phoenix layouts in responses to htmx requests.

Only available when Phoenix is available.

## Examples

The root layout (usually containing `<head>` content) is disabled for all htmx requests.

    iex> conn(:get, "/hello")
    ...> |> assign(:htmx, %{request: true})
    ...> |> Hypa.Plug.put_htmx_layout(Hypa.Plug.init([]))
    ...> |> Phoenix.Controller.root_layout("html")
    false

Usually, the app layout is disabled for htmx requests as well.

    iex> conn(:get, "/hello")
    ...> |> assign(:htmx, %{request: true})
    ...> |> Hypa.Plug.put_htmx_layout(Hypa.Plug.init([]))
    ...> |> Phoenix.Controller.layout("html")
    false

However, requests made using `hx-boost` are given the app layout.
The root layout is still disabled for boosted requests.

    iex> conn(:get, "/hello")
    ...> |> Phoenix.Controller.put_layout(html: {MyApp.Layouts, :app})
    ...> |> assign(:htmx, %{request: true, boosted: true})
    ...> |> Hypa.Plug.put_htmx_layout(Hypa.Plug.init([]))
    ...> |> Phoenix.Controller.layout("html")
    {MyApp.Layouts, :app}

This is also the case for history restore requests.
The root layout is disabled for history restore requests as well.

    iex> conn(:get, "/hello")
    ...> |> Phoenix.Controller.put_layout(html: {MyApp.Layouts, :app})
    ...> |> assign(:htmx, %{request: true, history_restore_request: true})
    ...> |> Hypa.Plug.put_htmx_layout(Hypa.Plug.init([]))
    ...> |> Phoenix.Controller.layout("html")
    {MyApp.Layouts, :app}

---

*Consult [api-reference.md](api-reference.md) for complete listing*
