defmodule Shino.UI.Input do @moduledoc """ Provides input related components. ## About main attrs All components in this module accept four main attrs: * `:field` - a `Phoenix.HTML.FormField` struct * `:id` * `:name` * `:value` When a `:field` attr is passed, `:id`, `:name` and `:value` can be retrieved from it. So, it's not necessary to specify them: ```heex ``` Otherwise `:name`, `:value` should be passed explicitly: ```heex ``` ## About file uploads For live file uploads, see `Phoenix.Component.live_file_input/1`. ## More information of inputs See for more information. Unsupported inputs are best written directly in your templates. ## Notes Differents from other components, input components are using `focus:`, instead of `focus-visible:`. ## References * [shadcn/ui - Input](https://ui.shadcn.com/docs/components/input) """ use Shino.UI, :component alias Shino.UI.Label @doc """ Renders an input. ## Examples ``` <.input name="email" "type="email" /> ``` Use a form field: ```heex <.input field={@form[:email]} type="email" placeholder="Enter your email" /> ``` """ attr :field, Phoenix.HTML.FormField attr :id, :any, default: nil attr :name, :any attr :value, :any attr :type, :string, default: "text", values: ~w(text email url password number range tel search color date time datetime-local month week file hidden) attr :class, :any, default: nil attr :rest, :global, # credo:disable-for-next-line # TODO: cleanup this list include: ~w(accept autocomplete capture cols disabled form list max maxlength min minlength multiple pattern placeholder readonly required rows size step) def input(%{type: "hidden"} = assigns) do ~H""" """ end def input(assigns) do assigns = prepare_assigns(assigns) ~H""" """ end @doc """ Renders a textarea. ## Examples ```heex <.textarea field={@form[:body]} class="resize-none" rows="6" /> ``` """ attr :field, Phoenix.HTML.FormField attr :id, :any, default: nil attr :name, :string attr :value, :string attr :class, :any, default: nil attr :rest, :global, include: ~w(rows) def textarea(assigns) do assigns = prepare_assigns(assigns) ~H""" """ end @doc """ Renders a checkbox. > In theory, this is this component should be implemented within the input > component, but to avoid complicating the attrs of input component, an > additional component was created. ## Notes To make this component pretty by default, extra style is added. If you find If you find that this component does not meet your needs, feel free to submit a PR. ## Examples ```heex <.checkbox field={@form[:remember_me]} label="Remember me" /> ``` """ attr :field, Phoenix.HTML.FormField attr :id, :any, default: nil attr :name, :any attr :value, :any attr :label, :string, default: nil attr :checked, :boolean attr :class, :any, default: nil attr :rest, :global def checkbox(assigns) do assigns = assigns |> prepare_assigns() |> assign_new(:checked, fn assigns -> Phoenix.HTML.Form.normalize_value("checkbox", assigns[:value]) end) ~H"""
<%= @label %>
""" end @doc """ Renders a switch. > This is a actually a checkbox with fancy style. > Some people also call it toggle. ## Examples ```heex <.switch field={@form[:force_update]} /> ``` """ attr :field, Phoenix.HTML.FormField attr :id, :any, default: nil attr :name, :any attr :value, :any attr :checked, :boolean attr :class, :any, default: nil attr :disabled, :boolean, default: false attr :rest, :global def switch(assigns) do assigns = assigns |> prepare_assigns() |> assign_new(:checked, fn assigns -> Phoenix.HTML.Form.normalize_value("checkbox", assigns[:value]) end) ~H""" """ end @doc """ Renders a select. ## Examples ```heex <.select field={@form[:time_range]} options={[ "Today": "today", "Yesterday": "yesterday", "Current Month": "current-month", "Current Year": "current-year" ]} /> ``` For the detail of `:options` attr, read the doc of `Phoenix.HTML.Form.options_for_select/2`. """ attr :field, Phoenix.HTML.FormField attr :id, :any, default: nil attr :name, :string attr :value, :string attr :prompt, :string, default: nil attr :options, :list, doc: "the options to pass to Phoenix.HTML.Form.options_for_select/2" attr :multiple, :boolean, default: false attr :class, :any, default: nil attr :rest, :global def select(assigns) do assigns = prepare_assigns(assigns) ~H""" """ end @doc """ Extends inputs with extra addons. ## Supported addons * `input_group_text/1` ## Compatible inputs * `input/1` * `select/1` ## Examples ```heex <.input_group> <.input_group_text>@ <.input name="name" value="" /> ``` Multiple inputs: ```heex <.input_group> <.input name="username" value="" /> <.input_group_text>@ <.input name="server" value="" /> ``` ## References * [Bootstrap > Forms > Input group](https://getbootstrap.com/docs/5.3/forms/input-group/) """ attr :class, :any, default: nil attr :rest, :global slot :inner_block, required: true def input_group(assigns) do ~H"""
:not(:first-child)]:rounded-l-none [&>:not(:first-child)]:-ml-px", "[&>:not(:last-child)]:rounded-r-none", "[&>.input-group-compatible]:relative", @class ]) } {@rest} > <%= render_slot(@inner_block) %>
""" end @doc """ Renders a text addon for input group. Read the doc of `input_group/1` for more information. """ attr :class, :any, default: nil attr :rest, :global slot :inner_block, required: true def input_group_text(assigns) do ~H"""
<%= render_slot(@inner_block) %>
""" end defp prepare_assigns(%{field: %Phoenix.HTML.FormField{} = field} = assigns) do assigns |> assign(field: nil, id: assigns.id || field.id) |> assign_new(:name, fn assigns -> if assigns[:multiple], do: field.name <> "[]", else: field.name end) |> assign_new(:value, fn -> field.value end) end defp prepare_assigns(assigns) do assigns end defp placeholder_class, do: "placeholder:text-muted-foreground" defp focus_class do [ "focus:outline-none", "focus:border-primary", "focus:ring focus:ring-offset-0 focus:ring-ring/10", "transition motion-reduce:transition-none ease-in-out duration-150" ] end defp disabled_class, do: "disabled:cursor-not-allowed disabled:opacity-50" end