lib/petal/form.ex

defmodule PetalComponents.Form do
  use Phoenix.Component
  import Phoenix.HTML.Form

  @moduledoc """
  Everything related to forms: inputs, labels etc
  """

  def plain_label(assigns) do
    ~H"""
    <label class={label_classes()}>
      <%= render_slot(@inner_block) %>
    </label>
    """
  end

  def form_label(assigns) do
    ~H"""
    <%= label @form, @field, class: label_classes(assigns) do %>
      <%= render_slot(@inner_block) %>
    <% end %>
    """
  end

  def text_input(assigns), do: input(Map.put(assigns, :type, "text"))
  def textarea(assigns), do: input(Map.put(assigns, :type, "textarea"))
  def select(assigns), do: input(Map.put(assigns, :type, "select"))
  def checkbox(assigns), do: input(Map.put(assigns, :type, "checkbox"))
  def radios(assigns), do: input(Map.put(assigns, :type, "radios"))

  defp input(assigns) do
    assigns = assign_new(assigns, :label, fn -> humanize(assigns.field) end)
    assigns = assign_new(assigns, :type, fn -> "text" end)

    assigns =
      assign_new(assigns, :input_attributes, fn ->
        Map.drop(assigns, [
          :label,
          :form,
          :field,
          :type,
          :options,
          :__changed__
        ])
        |> Map.to_list()
      end)

    build_input(assigns)
  end

  defp build_input(%{type: "text"} = assigns) do
    ~H"""
    <div class="mb-6">
      <.form_label form={@form} field={@field} type={@type}>
        <%= @label %>
      </.form_label>

      <%= text_input @form, @field, [class: text_input_classes(assigns)] ++ @input_attributes %>
    </div>
    """
  end

  defp build_input(%{type: "textarea"} = assigns) do
    ~H"""
    <div class="mb-6">
      <.form_label form={@form} field={@field} type={@type}>
        <%= @label %>
      </.form_label>

      <%= textarea @form, @field, Keyword.merge([
        class: text_input_classes(assigns),
        rows: "4"
      ], @input_attributes) %>
    </div>
    """
  end

  defp build_input(%{type: "select"} = assigns) do
    ~H"""
    <div class="mb-6">
      <.form_label form={@form} field={@field} type={@type}>
        <%= @label %>
      </.form_label>

      <%= select @form, @field, @options, [class: select_classes(assigns)] ++ @input_attributes %>
    </div>
    """
  end

  defp build_input(%{type: "checkbox"} = assigns) do
    ~H"""
    <div class="flex items-start mb-6">
      <div class="flex items-center h-5 ">
        <%= checkbox @form, @field, [class: checkbox_classes()] ++ @input_attributes %>
      </div>
      <div class="ml-3 text-sm">
        <.form_label form={@form} field={@field} type={@type}>
          <%= @label %>
        </.form_label>
      </div>
    </div>
    """
  end

  defp build_input(%{type: "radios"} = assigns) do
    ~H"""
    <.plain_label><%= @label %></.plain_label>

    <%= for {label, value} <- @options do %>
      <div class="flex items-start mb-1">
        <div class="flex items-center h-5">
          <%= radio_button @form, @field, value, [class: radio_classes()] ++ @input_attributes %>
        </div>
        <div class="ml-3 text-sm">
          <.form_label form={@form} field={@field} type={"radio"}>
            <%= label %>
          </.form_label>
        </div>
      </div>
    <% end %>
    """
  end

  defp label_classes(assigns \\ %{}) do
    type_classes =
      if Enum.member?(["checkbox", "radio"], assigns[:type]) do
        ""
      else
        "mb-2 font-medium"
      end

    "#{type_classes} text-sm text-gray-900 block dark:text-gray-200"
  end

  defp text_input_classes(assigns) do
    "#{assigns[:class] || ""} sm:text-sm block shadow-sm w-full rounded-md border-gray-300 dark:bg-gray-800 dark:text-gray-300 dark:border-gray-600 focus:border-primary-500 dark:focus:border-primary-500 focus:outline-none focus:ring-primary-500 focus:border-primary-500"
  end

  defp select_classes(assigns) do
    "#{assigns[:class] || ""} block w-full pl-3 pr-10 py-2 text-base border-gray-300 focus:outline-none focus:ring-primary-500 focus:border-primary-500 sm:text-sm rounded-md"
  end

  defp checkbox_classes() do
    "border-gray-300 rounded text-primary-700 w-5 h-5 ease-linear transition-all duration-150"
  end

  defp radio_classes() do
    "h-4 w-4 mt-0.5 cursor-pointer text-primary-600 border-gray-300 focus:ring-primary-500"
  end
end