lib/core_web/views/helpers.ex

defmodule Legendary.CoreWeb.Helpers do
  @moduledoc """
  HTML helpers for our styled (Tailwind) forms.
  """
  use Phoenix.HTML
  use Phoenix.Component

  alias Phoenix.Flash
  alias Legendary.AuthWeb.CoreComponents

  import ShorterMaps

  defdelegate current_user(a), to: Legendary.AuthWeb.Helpers
  defdelegate has_role?(a, b), to: Legendary.AuthWeb.Helpers

  def changeset_error_block(changeset) do
    assigns = %{changeset: changeset}

    ~H"""
    <CoreComponents.changeset_error_block changeset={@changeset} />
    """
  end

  def flash_block(%{assigns: assigns}) do
    ~H"""
    <div class="fixed w-full px-4 z-50 mt-20">
      <%= [info: "green", error: "red"] |> Enum.map(fn {level, color} -> %>
        <%= if Flash.get(@flash, level) do %>
          <div class={
            "relative bg-#{color}-100 lg:w-1/2 w-full p-5 object-right rounded shadow-xl m-auto mb-5 js-flash"
          }>
            <div class={"flex justify-between text-#{color}-700"}>
              <div class="flex space-x-3">
                <div class="flex-1 leading-tight text-sm font-medium">
                  <%= Flash.get(@flash, level) %>
                </div>
              </div>
              <div class="flex-none js-flash-closer">
                &times;
              </div>
            </div>
          </div>
        <% end %>
      <% end) %>
    </div>
    """
  end

  def styled_input(form, field, opts \\ [], options \\ nil, block_list \\ []) do
    {inner_block, _} = Keyword.pop(block_list, :do, []) || []
    {type, rest_opts} = Keyword.pop(opts, :type, input_type(form, field))
    {class, rest_opts} = Keyword.pop(rest_opts, :class)
    {label_text, rest_opts} = Keyword.pop(rest_opts, :label)
    {input_helper, _rest_opts} = Keyword.pop(rest_opts, :input_helper, input_type(form, field))

    error_class =
      if Keyword.get_values(form.errors || [], field) |> Enum.any?() do
        " border border-red-500"
      else
        ""
      end

    assigns =
      ~M{class, inner_block, error_class, field, input_helper, label_text, type, options, opts, form}

    ~H"""
    <CoreComponents.styled_input
      class={@class}
      error_class={@error_class}
      field={@field}
      input_helper={@input_helper}
      label_text={@label_text}
      type={@type}
      options={@options}
      form={@form}
      {@opts}
    >
      <%= @inner_block %>
    </CoreComponents.styled_input>
    """
  end

  def styled_button(inner_block, opts \\ []) do
    assigns = ~M{inner_block, opts}

    ~H"""
    <CoreComponents.styled_button {@opts}>
      <%= @inner_block %>
    </CoreComponents.styled_button>
    """
  end

  def styled_button_link(inner_block, opts) do
    {href, opts} = Keyword.pop(opts, :to, "#")
    rest =
      opts
      |> Enum.into(%{})
      |> Map.merge(~M{href})
    assigns = ~M{inner_block, rest}

    ~H"""
    <CoreComponents.styled_button_link {@rest}>
      <%= @inner_block %>
    </CoreComponents.styled_button_link>
    """
  end

  def styled_button_live_patch(inner_block, opts) do
    {to, opts} = Keyword.pop(opts, :to, "#")
    assigns = ~M{inner_block, opts, to}

    ~H"""
    <CoreComponents.styled_button_live_patch opts={@opts} to={@to}>
      <%= @inner_block %>
    </CoreComponents.styled_button_live_patch>
    """
  end

  def styled_button_live_redirect(inner_block, opts) do
    {to, opts} = Keyword.pop(opts, :to, "#")
    assigns = ~M{inner_block, opts, to}

    ~H"""
    <CoreComponents.styled_button_live_redirect opts={@opts} to={@to}>
      <%= @inner_block %>
    </CoreComponents.styled_button_live_redirect>
    """
  end

  def paginator(~M{first, current_page, last} = assigns) do
    pages =
      [first, current_page - 1, current_page, current_page + 1, last]
      |> Enum.sort()
      |> Enum.filter(&(&1 >= first && &1 <= last))
      |> Enum.dedup()

    assigns = assigns |> Map.put(:pages, pages)

    ~H"""
    <%= for page_number <- @pages do %>
      <%= @callback.(@first..@last, page_number) %>
    <% end %>
    """
  end

  def group_rounding_class(
        first..last,
        current,
        [first_class, middle_class, last_class] \\ ["rounded-l", "", "rounded-r"]
      ) do
    cond do
      first == last -> "#{first_class} #{last_class}"
      current == first -> first_class
      current == last -> last_class
      true -> middle_class
    end
  end

  @spec floating_form(any, atom | %{action: any}, [{:do, any}, ...]) :: {:safe, [...]}
  def floating_form(title, changeset, do: inner_block) do
    assigns = ~M{changeset, inner_block, title}

    ~H"""
    <CoreComponents.floating_form changeset={@changeset} title={@title}>
      <%= @inner_block %>
    </CoreComponents.floating_form>
    """
  end

  def floating_page_wrapper(do: inner_block) do
    assigns = ~M{inner_block}

    ~H"""
    <CoreComponents.floating_page_wrapper>
      <%= @inner_block %>
    </CoreComponents.floating_page_wrapper>
    """
  end

  def pow_extension_enabled?(extension) do
    {extensions, _rest} = Application.get_env(:core, :pow) |> Keyword.pop(:extensions, [])

    Enum.any?(extensions, &(&1 == extension))
  end
end