defmodule FlowbitePhoenix.Components.Forms do @moduledoc """ Form components for FlowbitePhoenix using Flowbite CSS framework. This module provides form-related components including inputs, buttons, toggles, and form containers with consistent Flowbite styling. """ use Phoenix.Component alias Phoenix.LiveView.JS @doc """ Renders a simple form with Flowbite styling. ## Examples <.simple_form for={@form} phx-change="validate" phx-submit="save"> <.input field={@form[:email]} label="Email"/> <.input field={@form[:username]} label="Username" /> <:actions> <.button>Save """ attr :for, :any, required: true, doc: "the data structure for the form" attr :as, :any, default: nil, doc: "the server side parameter to collect all input under" attr :rest, :global, include: ~w(autocomplete name rel action enctype method novalidate target multipart), doc: "the arbitrary HTML attributes to apply to the form tag" slot :inner_block, required: true slot :actions, doc: "the slot for form actions, such as a submit button" def simple_form(assigns) do ~H""" <.form :let={f} for={@for} as={@as} {@rest}>
{render_slot(@inner_block, f)}
{render_slot(action, f)}
""" end @doc """ Renders a button with Flowbite styling and multiple variants. ## Examples <.button>Send! <.button phx-click="go" color="alternative">Cancel <.button loading={true} color="blue">Loading... <.button variant="outline" color="red">Delete """ attr :type, :string, default: nil attr :class, :string, default: nil attr :loading, :boolean, default: false attr :variant, :string, default: "default", values: ~w(default outline) attr :color, :string, default: "blue", values: ~w(blue alternative dark light green red yellow purple) attr :size, :string, default: "default", values: ~w(xs sm default lg xl) attr :rest, :global, include: ~w(disabled form name value) slot :inner_block, required: true def button(assigns) do ~H""" """ end # Default button variants (filled) defp button_variant_class("default", "blue"), do: "text-white bg-blue-700 hover:bg-blue-800 focus:ring-blue-300 dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800" defp button_variant_class("default", "alternative"), do: "text-gray-900 bg-white border border-gray-200 hover:bg-gray-100 hover:text-blue-700 focus:ring-gray-200 dark:focus:ring-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700" defp button_variant_class("default", "dark"), do: "text-white bg-gray-800 hover:bg-gray-900 focus:ring-gray-300 dark:bg-gray-800 dark:hover:bg-gray-700 dark:focus:ring-gray-700 dark:border-gray-700" defp button_variant_class("default", "light"), do: "text-gray-900 bg-white border border-gray-300 hover:bg-gray-100 focus:ring-gray-200 dark:bg-gray-800 dark:text-white dark:border-gray-600 dark:hover:bg-gray-700 dark:hover:border-gray-600 dark:focus:ring-gray-700" defp button_variant_class("default", "green"), do: "text-white bg-green-700 hover:bg-green-800 focus:ring-green-300 dark:bg-green-600 dark:hover:bg-green-700 dark:focus:ring-green-800" defp button_variant_class("default", "red"), do: "text-white bg-red-700 hover:bg-red-800 focus:ring-red-300 dark:bg-red-600 dark:hover:bg-red-700 dark:focus:ring-red-900" defp button_variant_class("default", "yellow"), do: "text-white bg-yellow-400 hover:bg-yellow-500 focus:ring-yellow-300 dark:focus:ring-yellow-900" defp button_variant_class("default", "purple"), do: "text-white bg-purple-700 hover:bg-purple-800 focus:ring-purple-300 dark:bg-purple-600 dark:hover:bg-purple-700 dark:focus:ring-purple-900" # Outline button variants defp button_variant_class("outline", "blue"), do: "text-blue-700 hover:text-white border border-blue-700 hover:bg-blue-800 focus:ring-blue-300 dark:border-blue-500 dark:text-blue-500 dark:hover:text-white dark:hover:bg-blue-500 dark:focus:ring-blue-800" defp button_variant_class("outline", "dark"), do: "text-gray-900 hover:text-white border border-gray-800 hover:bg-gray-900 focus:ring-gray-300 dark:border-gray-600 dark:text-gray-400 dark:hover:text-white dark:hover:bg-gray-600 dark:focus:ring-gray-800" defp button_variant_class("outline", "green"), do: "text-green-700 hover:text-white border border-green-700 hover:bg-green-800 focus:ring-green-300 dark:border-green-500 dark:text-green-500 dark:hover:text-white dark:hover:bg-green-600 dark:focus:ring-green-800" defp button_variant_class("outline", "red"), do: "text-red-700 hover:text-white border border-red-700 hover:bg-red-800 focus:ring-red-300 dark:border-red-500 dark:text-red-500 dark:hover:text-white dark:hover:bg-red-600 dark:focus:ring-red-900" defp button_variant_class("outline", "yellow"), do: "text-yellow-400 hover:text-white border border-yellow-400 hover:bg-yellow-500 focus:ring-yellow-300 dark:border-yellow-300 dark:text-yellow-300 dark:hover:text-white dark:hover:bg-yellow-400 dark:focus:ring-yellow-900" defp button_variant_class("outline", "purple"), do: "text-purple-700 hover:text-white border border-purple-700 hover:bg-purple-800 focus:ring-purple-300 dark:border-purple-400 dark:text-purple-400 dark:hover:text-white dark:hover:bg-purple-500 dark:focus:ring-purple-900" # Default fallback for outline variants not specified defp button_variant_class("outline", _), do: "text-blue-700 hover:text-white border border-blue-700 hover:bg-blue-800 focus:ring-blue-300 dark:border-blue-500 dark:text-blue-500 dark:hover:text-white dark:hover:bg-blue-500 dark:focus:ring-blue-800" defp button_size_class(size) do FlowbitePhoenix.Config.get([:button, :sizes, String.to_atom(size)]) || case size do "xs" -> "px-3 py-2 text-xs" "sm" -> "px-3 py-2 text-sm" "default" -> "px-5 py-2.5 text-sm" "lg" -> "px-5 py-3 text-base" "xl" -> "px-6 py-3.5 text-base" end end @doc """ Renders an input with label and error messages using Flowbite styling. A `Phoenix.HTML.FormField` may be passed as argument, which is used to retrieve the input name, id, and values. Otherwise all attributes may be passed explicitly. ## Types This function accepts all HTML input types, considering that: * You may also set `type="select"` to render a ` <.error :for={msg <- @errors}>{msg} """ end def input(%{type: "select"} = assigns) do ~H"""
<.label for={@id}>{@label} <.error :for={msg <- @errors}>{@label} {msg}
""" end def input(%{type: "textarea"} = assigns) do ~H"""
<.label for={@id}>{@label} <.error :for={msg <- @errors}>{@label} {msg}
""" end # All other inputs text, datetime-local, url, password, etc. are handled here... def input(assigns) do ~H"""
<.label for={@id}>{@label} <.error :for={msg <- @errors}>{@label} {msg}
""" end @doc """ Renders a label with Flowbite styling. """ attr :for, :string, default: nil slot :inner_block, required: true def label(assigns) do ~H""" """ end @doc """ Generates a generic error message with Flowbite styling. """ slot :inner_block, required: true def error(assigns) do ~H"""

{render_slot(@inner_block)}

""" end @doc """ Renders a toggle switch component using Flowbite styling. ## Examples <.toggle name="notifications" label="Enable notifications" /> <.toggle name="dark_mode" label="Dark mode" checked={true} /> """ attr :id, :string, default: nil attr :name, :string, required: true attr :label, :string, default: nil attr :checked, :boolean, default: false attr :class, :string, default: "" attr :rest, :global def toggle(assigns) do ~H""" """ end end