Corex.SignaturePad (Corex v0.1.0-alpha.29)

View Source

Phoenix implementation of Zag.js Signature Pad.

Examples

Basic Usage

<.signature_pad id="my-signature-pad" class="signature-pad">
  <:label>Sign here</:label>
  <:clear_trigger>
    <.icon name="hero-x-mark" />
  </:clear_trigger>
</.signature_pad>

With Callback

<.signature_pad
  id="my-signature-pad"
  on_draw_end="signature_drawn"
  class="signature-pad">
  <:label>Sign here</:label>
  <:clear_trigger>
    <.icon name="hero-x-mark" />
  </:clear_trigger>
</.signature_pad>
def handle_event("signature_drawn", %{"paths" => paths}, socket) do
  {:noreply, put_flash(socket, :info, "Signature drawn with #{length(paths)} paths")}
end

Custom Drawing Options

<.signature_pad
  id="my-signature-pad"
  drawing_fill="blue"
  drawing_size={3}
  drawing_simulate_pressure
  class="signature-pad">
  <:label>Sign here</:label>
  <:clear_trigger>
    <.icon name="hero-x-mark" />
  </:clear_trigger>
</.signature_pad>

Phoenix Form Integration

When using with Phoenix forms, you must add an id to the form using the Corex.Form.get_form_id/1 function.

Controller

defmodule MyAppWeb.PageController do
  use MyAppWeb, :controller

  def home(conn, params) do
    form = Phoenix.Component.to_form(Map.get(params, "user", %{}), as: :user)
    render(conn, :home, form: form)
  end
end
<.form :let={f} as={:user} for={@form} id={get_form_id(@form)} method="get">
  <.signature_pad field={f[:signature]} id="my-signature-pad" class="signature-pad">
    <:label>Sign here</:label>
    <:clear_trigger>
      <.icon name="hero-x-mark" />
    </:clear_trigger>
    <:error :let={msg}>
      <.icon name="hero-exclamation-circle" class="icon" />
      {msg}
    </:error>
  </.signature_pad>
  <button type="submit">Submit</button>
</.form>

Live View

When using Phoenix form in a Live view you must also add controlled mode. This allows the Live view to be the source of truth and the component to be in sync accordingly.

defmodule MyAppWeb.SignaturePadLive do
  use MyAppWeb, :live_view

  def mount(_params, _session, socket) do
    form = to_form(%{"signature" => nil}, as: :user)
    {:ok, assign(socket, :form, form)}
  end

  def render(assigns) do
    ~H"""
    <.form as={:user} for={@form} id={get_form_id(@form)}>
      <.signature_pad
        field={@form[:signature]}
        id="my-signature-pad"
        class="signature-pad"
        controlled
      >
        <:label>Sign here</:label>
        <:clear_trigger>
          <.icon name="hero-x-mark" />
        </:clear_trigger>
        <:error :let={msg}>
          <.icon name="hero-exclamation-circle" class="icon" />
          {msg}
        </:error>
      </.signature_pad>
      <button type="submit">Submit</button>
    </.form>
    """
  end
end

With Ecto changeset

First create your schema and changeset:

defmodule MyApp.Accounts.User do
  use Ecto.Schema
  import Ecto.Changeset

  schema "users" do
    field :name, :string
    field :signature, :text
    timestamps(type: :utc_datetime)
  end

  def changeset(user, attrs) do
    user
    |> cast(attrs, [:name, :signature])
    |> validate_required([:name, :signature])
  end
end
defmodule MyAppWeb.UserLive do
  use MyAppWeb, :live_view
  alias MyApp.Accounts.User

  def mount(_params, _session, socket) do
    {:ok, assign(socket, :form, to_form(User.changeset(%User{}, %{})))}
  end

  def handle_event("validate", %{"user" => user_params}, socket) do
    changeset = User.changeset(%User{}, user_params)
    {:noreply, assign(socket, form: to_form(changeset, action: :validate))}
  end

  def render(assigns) do
    ~H"""
    <.form for={@form} id={get_form_id(@form)} phx-change="validate">
      <.signature_pad
        field={@form[:signature]}
        id="my-signature-pad"
        class="signature-pad"
      >
        <:label>Sign here</:label>
        <:clear_trigger>
          <.icon name="hero-x-mark" />
        </:clear_trigger>
        <:error :let={msg}>
          <.icon name="hero-exclamation-circle" class="icon" />
          {msg}
        </:error>
      </.signature_pad>
    </.form>
    """
  end
end

API Control

In order to use the API, you must use an id on the component

Client-side

<button phx-click={Corex.SignaturePad.clear("my-signature-pad")}>
  Clear Signature
</button>

Server-side

def handle_event("clear_signature", _, socket) do
  {:noreply, Corex.SignaturePad.clear(socket, "my-signature-pad")}
end

Styling

Use data attributes to target elements:

[data-scope="signature-pad"][data-part="root"] {}
[data-scope="signature-pad"][data-part="label"] {}
[data-scope="signature-pad"][data-part="control"] {}
[data-scope="signature-pad"][data-part="segment"] {}
[data-scope="signature-pad"][data-part="guide"] {}
[data-scope="signature-pad"][data-part="clear-trigger"] {}
[data-scope="signature-pad"][data-part="hidden-input"] {}

If you wish to use the default Corex styling, you can use the class signature-pad on the component. This requires to install Mix.Tasks.Corex.Design first and import the component css file.

@import "../corex/main.css";
@import "../corex/tokens/themes/neo/light.css";
@import "../corex/components/signature-pad.css";

You can then use modifiers

<.signature_pad class="signature-pad signature-pad--accent signature-pad--lg">

Learn more about modifiers and Corex Design

Summary

Components

Renders a signature pad component.

API

Clears the signature pad from client-side. Returns a Phoenix.LiveView.JS command.

Clears the signature pad from server-side. Pushes a LiveView event.

Components

signature_pad(assigns)

Renders a signature pad component.

Attributes

  • id (:string) - The id of the signature pad, useful for API to identify the signature pad.
  • drawing_fill (:string) - The fill color for drawing strokes. Defaults to "black".
  • drawing_size (:integer) - The size/thickness of drawing strokes. Defaults to 2.
  • drawing_simulate_pressure (:boolean) - Whether to simulate pressure for drawing strokes. Defaults to false.
  • drawing_smoothing (:float) - Smoothing factor for drawing strokes (0–1, perfect-freehand option). Defaults to 0.9.
  • drawing_easing (:string) - Easing function for drawing strokes (perfect-freehand option). Defaults to nil.
  • drawing_thinning (:float) - Thinning factor for drawing strokes (perfect-freehand option). Defaults to nil.
  • drawing_streamline (:float) - Streamline factor for drawing strokes (perfect-freehand option). Defaults to 0.1.
  • dir (:string) - The direction of the signature pad. When nil, derived from document (html lang + config :rtl_locales). Defaults to nil. Must be one of nil, "ltr", or "rtl".
  • on_draw_end (:string) - The server event name when drawing ends. Defaults to nil.
  • on_draw_end_client (:string) - The client event name when drawing ends. Defaults to nil.
  • controlled (:boolean) - Whether the signature pad is controlled. Defaults to false.
  • paths (:any) - The initial paths or the controlled paths of the signature pad. Can be a list or a JSON string. Defaults to nil.
  • name (:string) - The name of the signature pad input for form submission.
  • errors (:list) - List of error messages to display. Defaults to [].
  • field (Phoenix.HTML.FormField) - A form field struct retrieved from the form, for example: @form[:signature]. Automatically sets id, name, value, and errors from the form field.
  • Global attributes are accepted.

Slots

  • label - Accepts attributes:
    • class (:string)
  • clear_trigger - Accepts attributes:
    • class (:string)
    • aria_label (:string) - Accessibility label for the clear button. Defaults to 'Clear signature' if not provided.
  • error - Accepts attributes:
    • class (:string)

API

clear(signature_pad_id)

Clears the signature pad from client-side. Returns a Phoenix.LiveView.JS command.

Examples

<button phx-click={Corex.SignaturePad.clear("my-signature-pad")}>
  Clear
</button>

clear(socket, signature_pad_id)

Clears the signature pad from server-side. Pushes a LiveView event.

Examples

def handle_event("clear_signature", _params, socket) do
  {:noreply, Corex.SignaturePad.clear(socket, "my-signature-pad")}
end