Packages
ash_authentication_phoenix
2.16.0
3.0.0-rc.9
3.0.0-rc.8
3.0.0-rc.7
3.0.0-rc.6
3.0.0-rc.4
3.0.0-rc.3
3.0.0-rc.2
3.0.0-rc.1
3.0.0-rc.0
2.17.2
2.17.1
2.17.0
2.16.0
2.15.0
2.14.1
2.14.0
2.13.1
2.13.0
2.12.2
2.12.1
2.12.0
2.11.0
2.10.5
2.10.4
2.10.3
2.10.2
2.10.1
2.10.0
2.9.0
2.8.0
2.7.0
2.6.3
2.6.2
2.6.1
2.6.0
2.5.4
2.5.3
2.5.2
2.5.1
2.5.0
2.4.8
2.4.7
2.4.6
2.4.5
2.4.4
2.4.3
2.4.2
2.4.1
2.4.0
2.3.0
2.2.1
2.2.0
2.1.11
2.1.10
2.1.9
2.1.8
2.1.7
2.1.6
2.1.5
2.1.4
2.1.3
2.1.2
2.1.1
2.1.0
2.0.2
2.0.1
2.0.0
2.0.0-rc.2
2.0.0-rc.1
2.0.0-rc.0
1.9.4
1.9.3
1.9.2
1.9.1
1.9.0
1.8.7
1.8.6
1.8.5
1.8.4
1.8.3
1.8.2
1.8.1
1.8.0
1.7.3
1.7.2
1.7.1
1.7.0
1.6.6
1.6.5
1.6.4
1.6.3
1.6.2
1.6.1
1.6.0
1.5.1
1.5.0
1.4.8
1.4.7
1.4.6
1.4.5
1.4.4
1.4.3
1.4.2
1.4.1
1.4.0
1.3.1
1.3.0
1.2.0
1.1.0
1.0.1
1.0.0
Phoenix integration for Ash Authentication
Current section
Files
Jump to
Current section
Files
lib/ash_authentication_phoenix/components/password/input.ex
# SPDX-FileCopyrightText: 2022 Alembic Pty Ltd
#
# SPDX-License-Identifier: MIT
defmodule AshAuthentication.Phoenix.Components.Password.Input do
use AshAuthentication.Phoenix.Overrides.Overridable,
field_class: "CSS class for `div` elements surrounding the fields.",
label_class: "CSS class for `label` elements.",
input_class: "CSS class for text/password `input` elements.",
identity_input_label: "Label for identity field.",
identity_input_placeholder: "Placeholder for identity field.",
password_input_label: "Label for password field.",
password_confirmation_input_label: "Label for password confirmation field.",
input_class_with_error:
"CSS class for text/password `input` elements when there is a validation error.",
submit_class: "CSS class for the form submit `input` element.",
error_ul: "CSS class for the `ul` element on error lists.",
error_li: "CSS class for the `li` elements on error lists.",
input_debounce: "Number of milliseconds to debounce input by (or `nil` to disable).",
remember_me_class: "CSS class for the `div` element surrounding the remember me field.",
remember_me_input_label: "Label for remember me field.",
checkbox_class: "CSS class for the `input` element of the remember me field.",
checkbox_label_class: "CSS class for the `label` element of the remember me field."
@moduledoc """
Function components for dealing with form input during password
authentication.
## Component hierarchy
These function components are consumed by
`AshAuthentication.Phoenix.Components.Password.SignInForm`,
`AshAuthentication.Phoenix.Components.Password.RegisterForm` and
`AshAuthentication.Phoenix.Components.ResetForm`.
#{AshAuthentication.Phoenix.Overrides.Overridable.generate_docs()}
"""
use AshAuthentication.Phoenix.Web, :component
alias AshAuthentication.Strategy
alias AshPhoenix.Form
alias Phoenix.LiveView.{Rendered, Socket}
import Phoenix.HTML.Form
import PhoenixHTMLHelpers.Form
@doc """
Generate a form field for the configured identity field.
## Props
* `socket` - Phoenix LiveView socket.
This is needed to be able to retrieve the correct CSS configuration.
Required.
* `strategy` - The configuration map as per
`AshAuthentication.authenticated_resources/1`.
Required.
* `form` - An `AshPhoenix.Form`.
Required.
* `input_type` - Either `:text` or `:email`.
If not set it will try and guess based on the name of the identity field.
* `overrides` - A list of override modules.
* `gettext_fn` - Optional text translation function.
"""
@spec identity_field(%{
required(:socket) => Socket.t(),
required(:strategy) => Strategy.t(),
required(:form) => Form.t(),
optional(:input_type) => :text | :email,
optional(:overrides) => [module],
optional(:gettext_fn) => {module, atom}
}) :: Rendered.t() | no_return
def identity_field(assigns) do
identity_field = assigns.strategy.identity_field
assigns =
assigns
|> assign(:identity_field, identity_field)
|> assign_new(:overrides, fn -> [AshAuthentication.Phoenix.Overrides.Default] end)
|> assign_new(:gettext_fn, fn -> nil end)
|> assign_new(:input_type, fn ->
identity_field
|> to_string()
|> String.contains?("email")
|> then(fn
true -> :email
_ -> :text
end)
end)
|> assign_new(:input_class, fn ->
if has_error?(assigns.form, identity_field) do
override_for(assigns.overrides, :input_class_with_error)
else
override_for(assigns.overrides, :input_class)
end
end)
~H"""
<div class={override_for(@overrides, :field_class)}>
{label(@form, @identity_field, _gettext(override_for(@overrides, :identity_input_label)),
class: override_for(@overrides, :label_class)
)}
{text_input(@form, @identity_field,
type: to_string(@input_type),
class: @input_class,
phx_debounce: override_for(@overrides, :input_debounce),
autofocus: "true",
placeholder: override_for(@overrides, :identity_input_placeholder)
)}
<.error form={@form} field={@identity_field} overrides={@overrides} />
</div>
"""
end
@doc """
Generate a form field for the configured password entry field.
## Props
* `socket` - Phoenix LiveView socket. This is needed to be able to retrieve
the correct CSS configuration. Required.
* `strategy` - The configuration map as per
`AshAuthentication.authenticated_resources/1`. Required.
* `form` - An `AshPhoenix.Form`. Required.
* `overrides` - A list of override modules.
* `gettext_fn` - Optional text translation function.
"""
@spec password_field(%{
required(:socket) => Socket.t(),
required(:strategy) => Strategy.t(),
required(:form) => Form.t(),
optional(:overrides) => [module],
optional(:gettext_fn) => {module, atom}
}) :: Rendered.t() | no_return
def password_field(assigns) do
password_field = assigns.strategy.password_field
assigns =
assigns
|> assign(:password_field, password_field)
|> assign_new(:overrides, fn -> [AshAuthentication.Phoenix.Overrides.Default] end)
|> assign_new(:gettext_fn, fn -> nil end)
|> assign_new(:input_class, fn ->
if has_error?(assigns.form, password_field) do
override_for(assigns.overrides, :input_class_with_error)
else
override_for(assigns.overrides, :input_class)
end
end)
~H"""
<div class={override_for(@overrides, :field_class)}>
{label(@form, @password_field, _gettext(override_for(@overrides, :password_input_label)),
class: override_for(@overrides, :label_class)
)}
{password_input(@form, @password_field,
class: @input_class,
value: input_value(@form, @password_field),
phx_debounce: override_for(@overrides, :input_debounce)
)}
<.error form={@form} field={@password_field} overrides={@overrides} />
</div>
"""
end
@doc """
Generate a form field for the configured password confirmation entry field.
## Props
* `socket` - Phoenix LiveView socket. This is needed to be able to retrieve
the correct CSS configuration. Required.
* `strategy` - The configuration map as per
`AshAuthentication.authenticated_resources/1`. Required.
* `form` - An `AshPhoenix.Form`. Required.
* `overrides` - A list of override modules.
* `gettext_fn` - Optional text translation function.
"""
@spec password_confirmation_field(%{
required(:socket) => Socket.t(),
required(:strategy) => Strategy.t(),
required(:form) => Form.t(),
optional(:overrides) => [module],
optional(:gettext_fn) => {module, atom}
}) :: Rendered.t() | no_return
def password_confirmation_field(assigns) do
password_confirmation_field = assigns.strategy.password_confirmation_field
assigns =
assigns
|> assign(:password_confirmation_field, password_confirmation_field)
|> assign_new(:overrides, fn -> [AshAuthentication.Phoenix.Overrides.Default] end)
|> assign_new(:gettext_fn, fn -> nil end)
|> assign_new(:input_class, fn ->
if has_error?(assigns.form, password_confirmation_field) do
override_for(assigns.overrides, :input_class_with_error)
else
override_for(assigns.overrides, :input_class)
end
end)
~H"""
<div class={override_for(@overrides, :field_class)}>
{label(
@form,
@password_confirmation_field,
_gettext(override_for(@overrides, :password_confirmation_input_label)),
class: override_for(@overrides, :label_class)
)}
{password_input(@form, @password_confirmation_field,
class: @input_class,
value: input_value(@form, @password_confirmation_field),
phx_debounce: override_for(@overrides, :input_debounce)
)}
<.error form={@form} field={@password_confirmation_field} overrides={@overrides} />
</div>
"""
end
@doc """
Generate a form field for the remember me field.
## Props
* `socket` - Phoenix LiveView socket. This is needed to be able to retrieve
the correct CSS configuration. Required.
* `strategy` - The configuration map as per
`AshAuthentication.authenticated_resources/1`. Required.
* `form` - An `AshPhoenix.Form`. Required.
* `name` - The name of the field. Defaults to `:remember_me`.
* `overrides` - A list of override modules.
* `gettext_fn` - Optional text translation function.
"""
@spec remember_me_field(%{
required(:socket) => Socket.t(),
required(:strategy) => Strategy.t(),
required(:form) => Form.t(),
optional(:name) => atom,
optional(:overrides) => [module],
optional(:gettext_fn) => {module, atom}
}) :: Rendered.t() | no_return
def remember_me_field(assigns) do
assigns =
assigns
|> assign_new(:name, fn -> :remember_me end)
|> assign_new(:overrides, fn -> [AshAuthentication.Phoenix.Overrides.Default] end)
|> assign_new(:gettext_fn, fn -> nil end)
|> assign_new(:checkbox_class, fn -> override_for(assigns.overrides, :checkbox_class) end)
|> assign_new(:label_class, fn -> override_for(assigns.overrides, :label_class) end)
~H"""
<div class={override_for(@overrides, :remember_me_class)}>
{checkbox(@form, @name,
class: @checkbox_class,
value: input_value(@form, @name),
phx_debounce: override_for(@overrides, :input_debounce)
)}
{label(
@form,
@name,
_gettext(override_for(@overrides, :remember_me_input_label)),
class: override_for(@overrides, :checkbox_label_class)
)}
</div>
"""
end
@doc """
Generate an form submit button.
## Props
* `socket` - Phoenix LiveView socket. This is needed to be able to retrieve
the correct CSS configuration. Required.
* `strategy` - The configuration map as per
`AshAuthentication.authenticated_resources/1`. Required.
* `form` - An `AshPhoenix.Form`. Required.
* `action` - Either `:sign_in`, `:register`, `:request_reset` or `:reset`. Required.
* `label` - The text to show in the submit label. Generated from the
configured action name (via `Phoenix.Naming.humanize/1`) if not supplied.
* `overrides` - A list of override modules.
* `gettext_fn` - Optional text translation function.
"""
@spec submit(%{
required(:socket) => Socket.t(),
required(:strategy) => Strategy.t(),
required(:form) => Form.t(),
required(:action) => :sign_in | :register | :request_reset | :reset,
optional(:label) => String.t(),
optional(:overrides) => [module],
optional(:gettext_fn) => {module, atom}
}) :: Rendered.t() | no_return
def submit(assigns) do
assigns =
assigns
|> assign_new(:overrides, fn -> [AshAuthentication.Phoenix.Overrides.Default] end)
|> assign_new(:gettext_fn, fn -> nil end)
|> assign_new(:label, fn ->
case assigns.action do
:reset ->
assigns.strategy.resettable
|> Kernel.||(%{})
|> Map.get(:password_reset_action_name, :reset)
|> to_string()
|> String.trim_trailing("_with_password")
:request_reset ->
assigns.strategy.resettable
|> Kernel.||(%{})
|> Map.get(:request_password_reset_action_name, :reset_request)
|> to_string()
|> String.trim_trailing("_with_password")
:sign_in ->
assigns.strategy.sign_in_action_name
|> to_string()
|> String.trim_trailing("_with_password")
:register ->
assigns.strategy.register_action_name
|> to_string()
|> String.trim_trailing("_with_password")
other ->
other
end
|> humanize()
end)
|> assign_new(:disable_text, fn -> nil end)
~H"""
{submit(_gettext(@label),
class: override_for(@overrides, :submit_class),
phx_disable_with: _gettext(@disable_text)
)}
"""
end
@doc """
Generate a list of errors for a field (if there are any).
## Props
* `socket` - Phoenix LiveView socket. This is needed to be able to retrieve
the correct CSS configuration. Required.
* `form` - An `AshPhoenix.Form`. Required.
* `field` - The field for which to retrieve the errors. Required.
* `overrides` - A list of override modules.
* `gettext_fn` - Optional text translation function.
"""
@spec error(%{
required(:socket) => Socket.t(),
required(:form) => Form.t(),
required(:field) => atom,
optional(:field_label) => String.Chars.t(),
optional(:errors) => [{atom, String.t()}],
optional(:gettext_fn) => {module, atom}
}) :: Rendered.t() | no_return
def error(assigns) do
assigns =
assigns
|> assign_new(:overrides, fn -> [AshAuthentication.Phoenix.Overrides.Default] end)
|> assign_new(:gettext_fn, fn -> nil end)
|> assign_new(:errors, fn ->
assigns.form
|> Form.errors()
|> Keyword.get_values(assigns.field)
end)
|> assign_new(:field_label, fn -> humanize(assigns.field) end)
~H"""
<%= if Enum.any?(@errors) do %>
<ul class={override_for(@overrides, :error_ul)}>
<%= for error <- @errors do %>
<li class={override_for(@overrides, :error_li)} phx-feedback-for={input_name(@form, @field)}>
{_gettext(error)}
</li>
<% end %>
</ul>
<% end %>
"""
end
defp has_error?(form, field) do
form
|> Form.errors()
|> Keyword.has_key?(field)
end
end