Corex.Checkbox (Corex v0.1.0-beta.3)

View Source

Phoenix implementation of Zag.js Checkbox.

API and events

Client DOM dispatches (handled by the Checkbox hook on the component root):

  • corex:checkbox:set-checkeddetail.checked must be a boolean; clears indeterminate.
  • corex:checkbox:toggle-checked — toggles checked.

Server pushes (from set_checked/3, toggle_checked/2):

  • checkbox_set_checked%{"id" => id, "checked" => boolean}
  • checkbox_toggle_checked%{"id" => id}

Declarative checked may be true, false, or :indeterminate (Zag CheckedState). Imperative set_checked/2 remains boolean-only.

Examples

Minimal

<.checkbox class="checkbox">
  <:label>Accept terms</:label>
</.checkbox>

Custom Control

<.checkbox class="checkbox">
  <:label>
    Accept the terms
  </:label>
  <:indicator>
    <.heroicon name="hero-check" />
  </:indicator>
  <:indeterminate>
    <.heroicon name="hero-minus" />
  </:indeterminate>
</.checkbox>

Custom Error

<.checkbox class="checkbox">
  <:label>
    Accept the terms
  </:label>
  <:error :let={msg}>
    <.heroicon name="hero-exclamation-circle" class="icon" />
    {msg}
  </:error>
</.checkbox>

Phoenix Form Integration

When using with Phoenix forms, set the form id in to_form/2 and use id={@form.id} on <.form>.

Controller

Build the form from an Ecto changeset and pass it to the template. Pass id into to_form/2 so the template can use id={@form.id}:

def checkbox_form_page(conn, _params) do
  form =
    %MyApp.Form.Terms{}
    |> MyApp.Form.Terms.changeset(%{})
    |> Phoenix.Component.to_form(as: :terms, id: "checkbox-form")
  render(conn, :checkbox_form_page, form: form)
end
<.form :let={f} for={@form} id={@form.id} action={@action} method="post">
  <.checkbox field={f[:terms]} class="checkbox">
    <:label>Accept terms</:label>
    <:error :let={msg}>
      <.heroicon name="hero-exclamation-circle" class="icon" />
      {msg}
    </:error>
  </.checkbox>
  <button type="submit">Submit</button>
</.form>

Live View

When using Phoenix form in a Live view you must also add controlled mode. Prefer building the form from an Ecto changeset (see "With Ecto changeset" below).

With Ecto changeset (LiveView)

When using an Ecto changeset for validation in a LiveView, enable the controlled attribute on the checkbox so the LiveView remains the source of truth.

Schema and changeset:

defmodule MyApp.Form.Terms do
  use Ecto.Schema
  import Ecto.Changeset

  embedded_schema do
    field :terms, :boolean, default: false
  end

  def changeset(terms, attrs \\ %{}) do
    terms
    |> cast(attrs, [:terms])
    |> validate_required([:terms])
    |> validate_acceptance(:terms)
  end
end

LiveView with validate and submit:

defmodule MyAppWeb.CheckboxFormLive do
  use MyAppWeb, :live_view
  alias MyApp.Form.Terms

  def mount(_params, _session, socket) do
    form = %Terms{} |> Terms.changeset(%{}) |> to_form(as: :terms, id: "checkbox-form-terms")
    {:ok, assign(socket, :form, form)}
  end

  def handle_event("validate", %{"terms" => params}, socket) do
    changeset = Terms.changeset(%Terms{}, params)
    {:noreply, assign(socket, :form, to_form(changeset, action: :validate, as: :terms, id: "checkbox-form-terms"))}
  end

  def handle_event("save", %{"terms" => params}, socket) do
    case Terms.changeset(%Terms{}, params) do
      %Ecto.Changeset{valid?: true} = _ ->
        {:noreply, assign(socket, :form, to_form(Terms.changeset(%Terms{}, %{}), as: :terms, id: "checkbox-form-terms"))}
      changeset ->
        {:noreply, assign(socket, :form, to_form(changeset, action: :insert, as: :terms, id: "checkbox-form-terms"))}
    end
  end

  def render(assigns) do
    ~H"""
    <.form for={@form} id={@form.id} phx-change="validate" phx-submit="save">
      <.checkbox field={@form[:terms]} class="checkbox" controlled>
        <:label>Accept terms</:label>
        <:error :let={msg}>
          <.heroicon name="hero-exclamation-circle" class="icon" />
          {msg}
        </:error>
      </.checkbox>
      <button type="submit">Submit</button>
    </.form>
    """
  end
end

API Control

# Client-side
<button phx-click={Corex.Checkbox.set_checked("my-checkbox", true)}>
  Check
</button>

<button phx-click={Corex.Checkbox.toggle_checked("my-checkbox")}>
  Toggle
</button>

# Server-side
def handle_event("check", _, socket) do
  {:noreply, Corex.Checkbox.set_checked(socket, "my-checkbox", true)}
end

def handle_event("toggle", _, socket) do
  {:noreply, Corex.Checkbox.toggle_checked(socket, "my-checkbox")}
end

Styling

Use data attributes to target elements:

[data-scope="checkbox"][data-part="root"] {}
[data-scope="checkbox"][data-part="control"] {}
[data-scope="checkbox"][data-part="label"] {}
[data-scope="checkbox"][data-part="hidden-input"] {}
[data-scope="checkbox"][data-part="error"] {}

If you wish to use the default Corex styling, you can use the class checkbox 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/checkbox.css";

You can then use modifiers

<.checkbox class="checkbox checkbox--accent checkbox--lg">

components/checkbox#modifiers)

Summary

Components

Renders a checkbox component.

API

Sets the checkbox checked state from client-side. Returns a Phoenix.LiveView.JS command.

Sets the checkbox checked state from server-side. Pushes a LiveView event.

Toggles the checkbox checked state from client-side. Returns a Phoenix.LiveView.JS command.

Toggles the checkbox checked state from server-side. Pushes a LiveView event.

Components

checkbox(assigns)

Renders a checkbox component.

Attributes

  • id (:string) - The id of the checkbox, useful for API to identify the checkbox.
  • checked (:any) - Checked state: true, false, or :indeterminate (Zag CheckedState). Form fields still use boolean. Defaults to false.
  • controlled (:boolean) - Whether the checkbox is controlled. Defaults to false.
  • name (:string) - The name of the checkbox input for form submission.
  • form (:string) - The form id to associate the checkbox with.
  • aria_label (:string) - The accessible label for the checkbox. Defaults to "Label".
  • disabled (:boolean) - Whether the checkbox is disabled. Defaults to false.
  • value (:string) - The value of the checkbox when checked. Defaults to "true".
  • dir (:string) - The direction of the checkbox. When nil, derived from document (html lang + config :rtl_locales). Defaults to nil. Must be one of nil, "ltr", or "rtl".
  • orientation (:string) - Layout orientation for CSS (vertical or horizontal). Defaults to "horizontal". Must be one of "vertical", or "horizontal".
  • read_only (:boolean) - Whether the checkbox is read-only. Defaults to false.
  • invalid (:boolean) - Whether the checkbox has validation errors. Defaults to false.
  • required (:boolean) - Whether the checkbox is required. Defaults to false.
  • on_checked_change (:string) - The server event name when the checked state changes. Defaults to nil.
  • on_checked_change_client (:string) - The client event name when the checked state changes. Defaults to nil.
  • 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[:email]. Automatically sets id, name, checked state, and errors from the form field.
  • Global attributes are accepted.

Slots

  • label - Accepts attributes:
    • class (:string)
  • indicator - Accepts attributes:
    • class (:string)
  • indeterminate - Accepts attributes:
    • class (:string)
  • error - Accepts attributes:
    • class (:string)

API

set_checked(checkbox_id, checked)

Sets the checkbox checked state from client-side. Returns a Phoenix.LiveView.JS command.

Examples

<button phx-click={Corex.Checkbox.set_checked("my-checkbox", true)}>
  Check
</button>

<button phx-click={Corex.Checkbox.set_checked("my-checkbox", false)}>
  Uncheck
</button>

set_checked(socket, checkbox_id, checked)

Sets the checkbox checked state from server-side. Pushes a LiveView event.

Examples

def handle_event("check", _params, socket) do
  socket = Corex.Checkbox.set_checked(socket, "my-checkbox", true)
  {:noreply, socket}
end

toggle_checked(checkbox_id)

Toggles the checkbox checked state from client-side. Returns a Phoenix.LiveView.JS command.

Examples

<button phx-click={Corex.Checkbox.toggle_checked("my-checkbox")}>
  Toggle
</button>

toggle_checked(socket, checkbox_id)

Toggles the checkbox checked state from server-side. Pushes a LiveView event.

Examples

def handle_event("toggle", _params, socket) do
  socket = Corex.Checkbox.toggle_checked(socket, "my-checkbox")
  {:noreply, socket}
end

Functions

checkbox_skeleton(assigns)

Attributes

  • skeleton_label (:boolean) - When true, renders a compact label-line placeholder (same line height band as the real checkbox label). Defaults to true.
  • dir (:string) - Same as checkbox: logical direction for layout. Defaults to nil. Must be one of nil, "ltr", or "rtl".
  • orientation (:string) - Same as checkbox: layout orientation for the skeleton root. Defaults to "horizontal". Must be one of "vertical", or "horizontal".
  • Global attributes are accepted.