defmodule PetalComponents.Form do
use Phoenix.Component
import PetalComponents.Helpers
alias Phoenix.HTML.Form
@form_attrs ~w(autocomplete autocorrect autocapitalize disabled form max maxlength min minlength list
pattern placeholder readonly required size step value name multiple prompt selected default year month day hour minute second builder options layout cols rows wrap checked accept)
@checkbox_form_attrs ~w(checked_value unchecked_value checked hidden_input) ++ @form_attrs
@moduledoc """
Everything related to forms: inputs, labels etc
Deprecated in favor of field.ex and input.ex, which use the new `%Phoenix.HTML.FormField{}` struct.
"""
attr(:form, :any, default: nil, doc: "")
attr(:field, :atom, default: nil, doc: "")
attr(:label, :string, default: nil, doc: "labels your field")
attr(:class, :string, doc: "CSS classes to add to your label")
slot(:inner_block, required: false)
attr(:rest, :global, include: ~w(for))
def form_label(assigns) do
assigns =
assigns
|> assign(:classes, label_classes(assigns))
~H"""
<%= if @form && @field do %>
<%= Form.label @form, @field, [class: @classes, phx_feedback_for: Form.input_name(@form, @field)] ++ Map.to_list(@rest) do %>
<%= render_slot(@inner_block) || @label || Form.humanize(@field) %>
<% end %>
<% else %>
<label class={@classes} {@rest}>
<%= render_slot(@inner_block) || @label || Form.humanize(@field) %>
</label>
<% end %>
"""
end
@input_types [
"text_input",
"email_input",
"number_input",
"password_input",
"search_input",
"telephone_input",
"url_input",
"time_input",
"time_select",
"date_input",
"date_select",
"datetime_local_input",
"datetime_select",
"color_input",
"file_input",
"range_input",
"textarea",
"select",
"checkbox",
"checkbox_group",
"radio_group",
"switch",
"hidden_input"
]
attr(:form, :any, doc: "the form object", required: true)
attr(:field, :atom, doc: "field in changeset / form", required: true)
attr(:label, :string, doc: "labels your field")
attr(:label_class, :string, default: nil, doc: "extra CSS for your label")
attr(:help_text, :string, default: nil, doc: "context/help for your field")
attr(:type, :string,
default: "text_input",
values: @input_types,
doc: "The type of input"
)
attr(:wrapper_classes, :string, default: "pc-form-field-wrapper", doc: "CSS class for wrapper")
attr :rest, :global, include: @form_attrs
@doc "Use this when you want to include the label and some margin."
def form_field(%{type: "hidden_input"} = assigns) do
~H"""
<.hidden_input form={@form} field={@field} {@rest} />
"""
end
def form_field(assigns) do
assigns =
assigns
|> assign_new(:label, fn ->
if assigns[:field] do
Form.humanize(assigns[:field])
else
nil
end
end)
~H"""
<div class={@wrapper_classes} phx-feedback-for={Form.input_name(@form, @field)}>
<%= case @type do %>
<% "checkbox" -> %>
<label class="pc-checkbox-label">
<.checkbox form={@form} field={@field} {@rest} />
<div class={
label_classes(%{form: @form, field: @field, type: "checkbox", class: @label_class})
}>
<%= @label %>
</div>
</label>
<% "switch" -> %>
<label class="pc-checkbox-label">
<.switch form={@form} field={@field} {@rest} />
<div class={
label_classes(%{form: @form, field: @field, type: "checkbox", class: @label_class})
}>
<%= @label %>
</div>
</label>
<% "checkbox_group" -> %>
<.form_label form={@form} field={@field} label={@label} class={@label_class} />
<.checkbox_group form={@form} field={@field} {@rest} />
<% "radio_group" -> %>
<.form_label form={@form} field={@field} label={@label} class={@label_class} />
<.radio_group form={@form} field={@field} {@rest} />
<% "text_input" -> %>
<.form_label form={@form} field={@field} label={@label} class={@label_class} />
<.text_input form={@form} field={@field} {@rest} />
<% "email_input" -> %>
<.form_label form={@form} field={@field} label={@label} class={@label_class} />
<.email_input form={@form} field={@field} {@rest} />
<% "number_input" -> %>
<.form_label form={@form} field={@field} label={@label} class={@label_class} />
<.number_input form={@form} field={@field} {@rest} />
<% "password_input" -> %>
<.form_label form={@form} field={@field} label={@label} class={@label_class} />
<.password_input form={@form} field={@field} {@rest} />
<% "search_input" -> %>
<.form_label form={@form} field={@field} label={@label} class={@label_class} />
<.search_input form={@form} field={@field} {@rest} />
<% "telephone_input" -> %>
<.form_label form={@form} field={@field} label={@label} class={@label_class} />
<.telephone_input form={@form} field={@field} {@rest} />
<% "url_input" -> %>
<.form_label form={@form} field={@field} label={@label} class={@label_class} />
<.url_input form={@form} field={@field} {@rest} />
<% "time_input" -> %>
<.form_label form={@form} field={@field} label={@label} class={@label_class} />
<.time_input form={@form} field={@field} {@rest} />
<% "time_select" -> %>
<.form_label form={@form} field={@field} label={@label} class={@label_class} />
<.time_select form={@form} field={@field} {@rest} />
<% "datetime_select" -> %>
<.form_label form={@form} field={@field} label={@label} class={@label_class} />
<.datetime_select form={@form} field={@field} {@rest} />
<% "datetime_local_input" -> %>
<.form_label form={@form} field={@field} label={@label} class={@label_class} />
<.datetime_local_input form={@form} field={@field} {@rest} />
<% "date_select" -> %>
<.form_label form={@form} field={@field} label={@label} class={@label_class} />
<.date_select form={@form} field={@field} {@rest} />
<% "date_input" -> %>
<.form_label form={@form} field={@field} label={@label} class={@label_class} />
<.date_input form={@form} field={@field} {@rest} />
<% "color_input" -> %>
<.form_label form={@form} field={@field} label={@label} class={@label_class} />
<.color_input form={@form} field={@field} {@rest} />
<% "file_input" -> %>
<.form_label form={@form} field={@field} label={@label} class={@label_class} />
<.file_input form={@form} field={@field} {@rest} />
<% "range_input" -> %>
<.form_label form={@form} field={@field} label={@label} class={@label_class} />
<.range_input form={@form} field={@field} {@rest} />
<% "textarea" -> %>
<.form_label form={@form} field={@field} label={@label} class={@label_class} />
<.textarea form={@form} field={@field} {@rest} />
<% "select" -> %>
<.form_label form={@form} field={@field} label={@label} class={@label_class} />
<.select form={@form} field={@field} {@rest} />
<% end %>
<.form_field_error form={@form} field={@field} />
<.form_help_text help_text={@help_text} />
</div>
"""
end
attr(:form, :any, default: nil, doc: "")
attr(:field, :atom, default: nil, doc: "")
attr(:label, :string, default: nil, doc: "labels your field")
attr(:class, :string, default: "", doc: "extra classes for the text input")
attr(:rest, :global, include: @form_attrs)
def text_input(assigns) do
assigns = assign_defaults(assigns, text_input_classes(field_has_errors?(assigns)))
~H"""
<%= Form.text_input(
@form,
@field,
[class: @classes, phx_feedback_for: Form.input_name(@form, @field)] ++ Map.to_list(@rest)
) %>
"""
end
attr(:form, :any, default: nil, doc: "")
attr(:field, :atom, default: nil, doc: "")
attr(:label, :string, default: nil, doc: "labels your field")
attr(:class, :string, default: "", doc: "extra classes for the text input")
attr(:rest, :global, include: @form_attrs)
def email_input(assigns) do
assigns = assign_defaults(assigns, text_input_classes(field_has_errors?(assigns)))
~H"""
<%= Form.email_input(
@form,
@field,
[class: @classes, phx_feedback_for: Form.input_name(@form, @field)] ++ Map.to_list(@rest)
) %>
"""
end
attr(:form, :any, default: nil, doc: "")
attr(:field, :atom, default: nil, doc: "")
attr(:label, :string, default: nil, doc: "labels your field")
attr(:class, :string, default: "", doc: "extra classes for the text input")
attr(:rest, :global, include: @form_attrs)
def number_input(assigns) do
assigns = assign_defaults(assigns, text_input_classes(field_has_errors?(assigns)))
~H"""
<%= Form.number_input(
@form,
@field,
[class: @classes, phx_feedback_for: Form.input_name(@form, @field)] ++ Map.to_list(@rest)
) %>
"""
end
attr(:form, :any, default: nil, doc: "")
attr(:field, :atom, default: nil, doc: "")
attr(:label, :string, default: nil, doc: "labels your field")
attr(:class, :string, default: "", doc: "extra classes for the text input")
attr(:rest, :global, include: @form_attrs)
def password_input(assigns) do
assigns = assign_defaults(assigns, text_input_classes(field_has_errors?(assigns)))
~H"""
<%= Form.password_input(
@form,
@field,
[class: @classes, phx_feedback_for: Form.input_name(@form, @field)] ++ Map.to_list(@rest)
) %>
"""
end
attr(:form, :any, default: nil, doc: "")
attr(:field, :atom, default: nil, doc: "")
attr(:label, :string, default: nil, doc: "labels your field")
attr(:class, :string, default: "", doc: "extra classes for the text input")
attr(:rest, :global, include: @form_attrs)
def search_input(assigns) do
assigns = assign_defaults(assigns, text_input_classes(field_has_errors?(assigns)))
~H"""
<%= Form.search_input(
@form,
@field,
[class: @classes, phx_feedback_for: Form.input_name(@form, @field)] ++ Map.to_list(@rest)
) %>
"""
end
attr(:form, :any, default: nil, doc: "")
attr(:field, :atom, default: nil, doc: "")
attr(:label, :string, default: nil, doc: "labels your field")
attr(:class, :string, default: "", doc: "extra classes for the text input")
attr(:rest, :global, include: @form_attrs)
def telephone_input(assigns) do
assigns = assign_defaults(assigns, text_input_classes(field_has_errors?(assigns)))
~H"""
<%= Form.telephone_input(
@form,
@field,
[class: @classes, phx_feedback_for: Form.input_name(@form, @field)] ++ Map.to_list(@rest)
) %>
"""
end
attr(:form, :any, default: nil, doc: "")
attr(:field, :atom, default: nil, doc: "")
attr(:label, :string, default: nil, doc: "labels your field")
attr(:class, :string, default: "", doc: "extra classes for the text input")
attr(:rest, :global, include: @form_attrs)
def url_input(assigns) do
assigns = assign_defaults(assigns, text_input_classes(field_has_errors?(assigns)))
~H"""
<%= Form.url_input(
@form,
@field,
[class: @classes, phx_feedback_for: Form.input_name(@form, @field)] ++ Map.to_list(@rest)
) %>
"""
end
attr(:form, :any, default: nil, doc: "")
attr(:field, :atom, default: nil, doc: "")
attr(:label, :string, default: nil, doc: "labels your field")
attr(:class, :string, default: "", doc: "extra classes for the text input")
attr(:rest, :global, include: @form_attrs)
def time_input(assigns) do
assigns = assign_defaults(assigns, text_input_classes(field_has_errors?(assigns)))
~H"""
<%= Form.time_input(
@form,
@field,
[class: @classes, phx_feedback_for: Form.input_name(@form, @field)] ++ Map.to_list(@rest)
) %>
"""
end
attr(:form, :any, default: nil, doc: "")
attr(:field, :atom, default: nil, doc: "")
attr(:label, :string, default: nil, doc: "labels your field")
attr(:class, :string, default: "", doc: "extra classes for the text input")
attr(:rest, :global, include: @form_attrs)
def time_select(assigns) do
assigns = assign_defaults(assigns, text_input_classes(field_has_errors?(assigns)))
~H"""
<div class="pc-time-select">
<%= Form.time_select(
@form,
@field,
[class: @classes, phx_feedback_for: Form.input_name(@form, @field)] ++ Map.to_list(@rest)
) %>
</div>
"""
end
attr(:form, :any, default: nil, doc: "")
attr(:field, :atom, default: nil, doc: "")
attr(:label, :string, default: nil, doc: "labels your field")
attr(:class, :string, default: "", doc: "extra classes for the text input")
attr(:rest, :global, include: @form_attrs)
def datetime_local_input(assigns) do
assigns = assign_defaults(assigns, text_input_classes(field_has_errors?(assigns)))
~H"""
<%= Form.datetime_local_input(
@form,
@field,
[class: @classes, phx_feedback_for: Form.input_name(@form, @field)] ++ Map.to_list(@rest)
) %>
"""
end
attr(:form, :any, default: nil, doc: "")
attr(:field, :atom, default: nil, doc: "")
attr(:label, :string, default: nil, doc: "labels your field")
attr(:class, :string, default: "", doc: "extra classes for the text input")
attr(:rest, :global, include: @form_attrs)
def datetime_select(assigns) do
assigns = assign_defaults(assigns, text_input_classes(field_has_errors?(assigns)))
~H"""
<div class="pc-datetime-select">
<%= Form.datetime_select(
@form,
@field,
[class: @classes, phx_feedback_for: Form.input_name(@form, @field)] ++ Map.to_list(@rest)
) %>
</div>
"""
end
attr(:form, :any, default: nil, doc: "")
attr(:field, :atom, default: nil, doc: "")
attr(:label, :string, default: nil, doc: "labels your field")
attr(:class, :string, default: "", doc: "extra classes for the text input")
attr(:rest, :global, include: @form_attrs)
def date_select(assigns) do
assigns = assign_defaults(assigns, text_input_classes(field_has_errors?(assigns)))
~H"""
<div class="pc-date-select">
<%= Form.date_select(
@form,
@field,
[class: @classes, phx_feedback_for: Form.input_name(@form, @field)] ++ Map.to_list(@rest)
) %>
</div>
"""
end
attr(:form, :any, default: nil, doc: "")
attr(:field, :atom, default: nil, doc: "")
attr(:label, :string, default: nil, doc: "labels your field")
attr(:class, :string, default: "", doc: "extra classes for the text input")
attr(:rest, :global, include: @form_attrs)
def date_input(assigns) do
assigns = assign_defaults(assigns, text_input_classes(field_has_errors?(assigns)))
~H"""
<%= Form.date_input(
@form,
@field,
[class: @classes, phx_feedback_for: Form.input_name(@form, @field)] ++ Map.to_list(@rest)
) %>
"""
end
attr(:form, :any, default: nil, doc: "")
attr(:field, :atom, default: nil, doc: "")
attr(:label, :string, default: nil, doc: "labels your field")
attr(:class, :string, default: "", doc: "extra classes for the text input")
attr(:rest, :global, include: @form_attrs)
def color_input(assigns) do
assigns = assign_defaults(assigns, color_input_classes(field_has_errors?(assigns)))
~H"""
<%= Form.color_input(
@form,
@field,
[class: @classes, phx_feedback_for: Form.input_name(@form, @field)] ++ Map.to_list(@rest)
) %>
"""
end
attr(:form, :any, default: nil, doc: "")
attr(:field, :atom, default: nil, doc: "")
attr(:label, :string, default: nil, doc: "labels your field")
attr(:class, :string, default: "", doc: "extra classes for the text input")
attr(:rest, :global, include: @form_attrs)
def file_input(assigns) do
assigns = assign_defaults(assigns, file_input_classes(field_has_errors?(assigns)))
~H"""
<%= Form.file_input(
@form,
@field,
[class: @classes, phx_feedback_for: Form.input_name(@form, @field)] ++ Map.to_list(@rest)
) %>
"""
end
attr(:form, :any, default: nil, doc: "")
attr(:field, :atom, default: nil, doc: "")
attr(:label, :string, default: nil, doc: "labels your field")
attr(:class, :string, default: "", doc: "extra classes for the text input")
attr(:rest, :global, include: @form_attrs)
def range_input(assigns) do
assigns = assign_defaults(assigns, range_input_classes(field_has_errors?(assigns)))
~H"""
<%= Form.range_input(
@form,
@field,
[class: @classes, phx_feedback_for: Form.input_name(@form, @field)] ++ Map.to_list(@rest)
) %>
"""
end
attr(:form, :any, default: nil, doc: "")
attr(:field, :atom, default: nil, doc: "")
attr(:label, :string, default: nil, doc: "labels your field")
attr(:class, :string, default: "", doc: "extra classes for the text input")
attr(:rest, :global, include: @form_attrs)
def textarea(assigns) do
assigns = assign_defaults(assigns, text_input_classes(field_has_errors?(assigns)))
~H"""
<%= Form.textarea(
@form,
@field,
[class: @classes, rows: "4", phx_feedback_for: Form.input_name(@form, @field)] ++
Map.to_list(@rest)
) %>
"""
end
attr(:form, :any, default: nil, doc: "")
attr(:field, :atom, default: nil, doc: "")
attr(:label, :string, default: nil, doc: "labels your field")
attr(:class, :string, default: "", doc: "extra classes for the text input")
attr(:options, :list, default: [], doc: "options for the select")
attr(:rest, :global, include: @form_attrs)
def select(assigns) do
assigns = assign_defaults(assigns, select_classes(field_has_errors?(assigns)))
~H"""
<%= Form.select(
@form,
@field,
@options,
[class: @classes, phx_feedback_for: Form.input_name(@form, @field)] ++ Map.to_list(@rest)
) %>
"""
end
attr(:form, :any, default: nil, doc: "")
attr(:field, :atom, default: nil, doc: "")
attr(:label, :string, default: nil, doc: "labels your field")
attr(:class, :string, default: "", doc: "extra classes for the text input")
attr(:rest, :global,
include: ~w(checked_value unchecked_value checked hidden_input) ++ @form_attrs
)
def checkbox(assigns) do
assigns = assign_defaults(assigns, checkbox_classes(field_has_errors?(assigns)))
~H"""
<%= Form.checkbox(
@form,
@field,
[class: @classes, phx_feedback_for: Form.input_name(@form, @field)] ++ Map.to_list(@rest)
) %>
"""
end
attr(:form, :any, default: nil, doc: "")
attr(:field, :atom, default: nil, doc: "")
attr(:label, :string, default: nil, doc: "labels your field")
attr(:class, :string, default: "", doc: "extra classes for the text input")
attr(:options, :list, default: [], doc: "options for the select")
attr(:layout, :atom, default: :col, values: [:row, :col], doc: "layout for the checkboxes")
attr(:checked, :list, doc: "a list of checked values")
attr(:rest, :global, include: @form_attrs)
def checkbox_group(assigns) do
assigns =
assigns
|> assign_defaults(checkbox_classes(field_has_errors?(assigns)))
|> assign_new(:checked, fn ->
values =
case Form.input_value(assigns[:form], assigns[:field]) do
value when is_binary(value) -> [value]
value when is_list(value) -> value
_ -> []
end
Enum.map(values, &to_string/1)
end)
|> assign_new(:id_prefix, fn -> Form.input_id(assigns[:form], assigns[:field]) <> "_" end)
~H"""
<div class={checkbox_group_layout_classes(%{layout: @layout})}>
<%= Form.hidden_input(@form, @field, name: Form.input_name(@form, @field), value: "") %>
<%= for {label, value} <- @options do %>
<label class={checkbox_group_layout_item_classes(%{layout: @layout})}>
<.checkbox
form={@form}
field={@field}
id={@id_prefix <> to_string(value)}
name={Form.input_name(@form, @field) <> "[]"}
checked_value={value}
unchecked_value=""
value={value}
checked={to_string(value) in @checked}
hidden_input={false}
{@rest}
/>
<div class={label_classes(%{form: @form, field: @field, type: "checkbox"})}>
<%= label %>
</div>
</label>
<% end %>
</div>
"""
end
attr(:form, :any, default: nil, doc: "")
attr(:field, :atom, default: nil, doc: "")
attr(:label, :string, default: nil, doc: "labels your field")
attr(:class, :string, default: "", doc: "extra classes for the text input")
attr(:rest, :global, include: @checkbox_form_attrs)
def switch(assigns) do
base_class = if field_has_errors?(assigns), do: "has-error", else: ""
assigns = assign_defaults(assigns, base_class)
~H"""
<label class="pc-switch">
<%= Form.checkbox(
@form,
@field,
[
class: "sr-only peer #{@classes}",
phx_feedback_for: Form.input_name(@form, @field)
] ++ Map.to_list(@rest)
) %>
<span class="pc-switch__fake-input"></span>
<span class="pc-switch__fake-input-bg"></span>
</label>
"""
end
attr(:form, :any, default: nil, doc: "")
attr(:field, :atom, default: nil, doc: "")
attr(:label, :string, default: nil, doc: "labels your field")
attr(:class, :string, default: "", doc: "extra classes for the text input")
attr(:value, :any, default: nil, doc: "the radio value")
attr(:rest, :global, include: @form_attrs)
def radio(assigns) do
assigns = assign_defaults(assigns, radio_classes(field_has_errors?(assigns)))
~H"""
<%= Form.radio_button(
@form,
@field,
@value,
[class: @classes, phx_feedback_for: Form.input_name(@form, @field)] ++ Map.to_list(@rest)
) %>
"""
end
attr(:form, :any, default: nil, doc: "")
attr(:field, :atom, default: nil, doc: "")
attr(:label, :string, default: nil, doc: "labels your field")
attr(:class, :string, default: "", doc: "extra classes for the text input")
attr(:options, :list, default: [], doc: "options for the select")
attr(:layout, :atom, default: :col, values: [:row, :col], doc: "layout for the radio options")
attr(:rest, :global, include: @form_attrs)
def radio_group(assigns) do
assigns =
assigns
|> assign_defaults(radio_classes(field_has_errors?(assigns)))
~H"""
<div class={radio_group_layout_classes(%{layout: @layout})}>
<%= for {label, value} <- @options do %>
<label class={radio_group_layout_item_classes(%{layout: @layout})}>
<.radio form={@form} field={@field} value={value} {@rest} />
<div class={label_classes(%{form: @form, field: @field, type: "radio"})}><%= label %></div>
</label>
<% end %>
</div>
"""
end
attr(:form, :any, default: nil, doc: "")
attr(:field, :atom, default: nil, doc: "")
attr(:rest, :global, include: @form_attrs)
def hidden_input(assigns) do
assigns = assign_defaults(assigns, text_input_classes(field_has_errors?(assigns)))
~H"""
<%= Form.hidden_input(
@form,
@field,
[phx_feedback_for: Form.input_name(@form, @field)] ++ Map.to_list(@rest)
) %>
"""
end
attr(:form, :any, default: nil, doc: "")
attr(:field, :atom, default: nil, doc: "")
attr(:class, :string, default: "", doc: "extra classes for the text input")
def form_field_error(assigns) do
assigns =
assigns
|> assign(:translated_errors, generated_translated_errors(assigns.form, assigns.field))
~H"""
<%= if field_has_errors?(assigns) do %>
<div class={@class}>
<%= for translated_error <- @translated_errors do %>
<div
class="pc-form-field-error invalid-feedback"
phx-feedback-for={Form.input_name(@form, @field)}
>
<%= translated_error %>
</div>
<% end %>
</div>
<% end %>
"""
end
attr(:class, :string, default: "", doc: "extra classes for the help text")
attr(:help_text, :string, default: nil, doc: "context/help for your field")
slot(:inner_block, required: false)
attr(:rest, :global)
def form_help_text(assigns) do
~H"""
<div :if={render_slot(@inner_block) || @help_text} class={["pc-form-help-text", @class]} {@rest}>
<%= render_slot(@inner_block) || @help_text %>
</div>
"""
end
defp generated_translated_errors(form, field) do
translate_error = translator_from_config() || (&translate_error/1)
Keyword.get_values(form.errors || [], field)
|> Enum.map(fn error ->
translate_error.(error)
end)
end
defp translate_error({msg, opts}) do
# Because the error messages we show in our forms and APIs
# are defined inside Ecto, we need to translate them dynamically.
Enum.reduce(opts, msg, fn {key, value}, acc ->
try do
String.replace(acc, "%{#{key}}", to_string(value))
rescue
e ->
IO.warn(
"""
the fallback message translator for the form_field_error function cannot handle the given value.
Hint: you can set up the `error_translator_function` to route all errors to your application helpers:
config :petal_components, :error_translator_function, {MyAppWeb.ErrorHelpers, :translate_error}
Given value: #{inspect(value)}
Exception: #{Exception.message(e)}
""",
__STACKTRACE__
)
"invalid value"
end
end)
end
defp translator_from_config do
case Application.get_env(:petal_components, :error_translator_function) do
{module, function} -> &apply(module, function, [&1])
nil -> nil
end
end
defp assign_defaults(assigns, base_classes) do
assigns
|> assign_new(:type, fn -> "text" end)
|> assign_new(:classes, fn ->
build_class([base_classes, assigns[:class]])
end)
end
defp label_classes(assigns) do
type_classes =
if Enum.member?(["checkbox", "radio"], assigns[:type]) do
"pc-label--for-checkbox"
else
""
end
"#{if field_has_errors?(assigns), do: "has-error", else: ""} #{type_classes} #{assigns[:class] || ""} pc-label"
end
defp text_input_classes(has_error) do
"#{if has_error, do: "has-error", else: ""} pc-text-input"
end
defp select_classes(has_error) do
"#{if has_error, do: "has-error", else: ""} pc-select"
end
defp file_input_classes(has_error) do
"#{if has_error, do: "has-error", else: ""} pc-file-input"
end
defp color_input_classes(has_error) do
"#{if has_error, do: "has-error", else: ""} pc-color-input"
end
defp range_input_classes(has_error) do
"#{if has_error, do: "has-error", else: ""} pc-range-input"
end
defp checkbox_classes(has_error) do
"#{if has_error, do: "has-error", else: ""} pc-checkbox"
end
defp checkbox_group_layout_classes(assigns) do
case assigns[:layout] do
:row ->
"pc-checkbox-group--row"
_col ->
"pc-checkbox-group--col"
end
end
defp checkbox_group_layout_item_classes(assigns) do
case assigns[:layout] do
:row ->
"pc-checkbox-group__item--row"
_col ->
"pc-checkbox-group__item--col"
end
end
defp radio_group_layout_classes(assigns) do
case assigns[:layout] do
:row ->
"pc-radio-group--row"
_col ->
"pc-radio-group--col"
end
end
defp radio_group_layout_item_classes(assigns) do
case assigns[:layout] do
:row ->
"pc-radio-group__item--row"
_col ->
"pc-radio-group__item--col"
end
end
defp radio_classes(has_error) do
"#{if has_error, do: "has-error", else: ""} pc-radio"
end
defp field_has_errors?(%{form: form, field: field}) when is_map(form) do
case Keyword.get_values(form.errors || [], field) do
[] -> false
_ -> true
end
end
defp field_has_errors?(_), do: false
end