defmodule Corex.PinInput do @moduledoc ~S''' Phoenix implementation of [Zag.js Pin Input](https://zagjs.com/components/react/pin-input). ## Examples ### Basic ```heex <.pin_input id="pin" count={4} class="pin-input"> <:label>Code ``` ## Styling Use data attributes to target elements: ```css [data-scope="pin-input"][data-part="root"] {} [data-scope="pin-input"][data-part="label"] {} [data-scope="pin-input"][data-part="control"] {} [data-scope="pin-input"][data-part="input"] {} ``` If you wish to use the default Corex styling, you can use the class `pin-input` on the component. This requires to install `Mix.Tasks.Corex.Design` first and import the component css file. ```css @import "../corex/main.css"; @import "../corex/tokens/themes/neo/light.css"; @import "../corex/components/pin-input.css"; ``` You can then use modifiers ```heex <.pin_input class="pin-input pin-input--accent pin-input--lg" count={4}> <:label>Code ``` Learn more about modifiers and [Corex Design](https://corex-ui.com/components/pin-input#modifiers) ''' defmodule Translation do @moduledoc """ Translation struct for PinInput component strings. Without gettext: `translation={%PinInput.Translation{ digit: "Box %{digit}" }}` With gettext: `translation={%PinInput.Translation{ digit: "Digit %{digit}" }}` (add to catalog; component calls gettext at render) """ defstruct [:digit] end @doc type: :component use Phoenix.Component alias Corex.PinInput.Anatomy.{Control, HiddenInput, Input, Label, Props, Root} alias Corex.PinInput.Connect import Corex.Gettext, only: [gettext: 2] import Corex.Helpers, only: [validate_value!: 1] attr(:id, :string, required: false) attr(:value, :list, default: [], doc: "Controlled value (list of single chars)") attr(:default_value, :list, default: [], doc: "Uncontrolled initial value") attr(:controlled, :boolean, default: false) attr(:count, :integer, default: 4, doc: "Number of input boxes") attr(:disabled, :boolean, default: false) attr(:invalid, :boolean, default: false) attr(:required, :boolean, default: false) attr(:read_only, :boolean, default: false) attr(:mask, :boolean, default: false) attr(:otp, :boolean, default: false) attr(:blur_on_complete, :boolean, default: false) attr(:select_on_focus, :boolean, default: false) attr(:name, :string, default: nil) attr(:form, :string, default: nil) attr(:dir, :string, default: nil, values: [nil, "ltr", "rtl"]) attr(:type, :string, default: "numeric", values: ["alphanumeric", "numeric", "alphabetic"]) attr(:placeholder, :string, default: "○") attr(:on_value_change, :string, default: nil) attr(:on_value_change_client, :string, default: nil) attr(:on_value_complete, :string, default: nil) attr(:translation, Corex.PinInput.Translation, default: nil, doc: "Override translatable strings" ) attr(:rest, :global) slot :label, required: false do attr(:class, :string, required: false) end def pin_input(assigns) do default_translation = %Translation{digit: "Digit %{digit}"} assigns = assigns |> assign_new(:id, fn -> "pin-input-#{System.unique_integer([:positive])}" end) |> assign_new(:dir, fn -> "ltr" end) |> assign_new(:translation, fn -> default_translation end) |> assign(:translation, merge_translation(assigns.translation, default_translation)) |> assign(:value, validate_value!(assigns[:value] || [])) |> assign(:default_value, validate_value!(assigns[:default_value] || [])) value_str = Enum.join(assigns.value, "") default_value_str = Enum.join(assigns.default_value, "") ~H"""
""" end defp merge_translation(nil, default), do: default defp merge_translation(partial, default) do %Translation{ digit: partial.digit || default.digit } end end