Corex.Editable (Corex v0.2.0)

View Source

Inline editable text for Phoenix LiveView forms. Behavior follows Zag.js Editable.

Anatomy

Basic

<.editable value="Click to edit" class="editable">
  <:label>Name</:label>
  <:edit_trigger><.heroicon name="hero-pencil-square" class="icon" /></:edit_trigger>
  <:submit_trigger><.heroicon name="hero-check" class="icon" /></:submit_trigger>
  <:cancel_trigger><.heroicon name="hero-x-mark" class="icon" /></:cancel_trigger>
</.editable>

Required slots: :label, :edit_trigger, :submit_trigger, :cancel_trigger. Preview value is managed by the component and the Editable TS hook.

API

Pass a stable id when using set_value/2 or set_value/3. Otherwise an id is generated automatically.

FunctionActionReturns
set_value/2Set preview value (client)%Phoenix.LiveView.JS{}
set_value/3Set preview value (server)socket

Events

Pick an event name and pass it to on_* on <.editable>.

Server events

EventWhenPayload
on_value_change="editable_changed"Value committed or cancelled%{"id" => id, "value" => string}

on_value_change

<.editable
  value="Click to edit"
  class="editable"
  on_value_change="editable_changed"
>
  <:label>Name</:label>
  <:edit_trigger><.heroicon name="hero-pencil-square" class="icon" /></:edit_trigger>
  <:submit_trigger><.heroicon name="hero-check" class="icon" /></:submit_trigger>
  <:cancel_trigger><.heroicon name="hero-x-mark" class="icon" /></:cancel_trigger>
</.editable>
def handle_event("editable_changed", %{"id" => _id, "value" => value}, socket) do
  {:noreply, assign(socket, :name, value)}
end

Client events

EventWhenevent.detail
on_value_change_client="editable-changed"Value changesid, value

Form

Use field={f[:name]} inside <.form> for changeset-backed forms.

For cross-cutting invalid styling and error presentation, see the Forms guide. With field={@form[:…]}, pass auto_invalid for alert borders from visible errors, or invalid={true} to force the alert state.

<.form for={@form} phx-change="validate">
  <.editable field={@form[:name]} class="editable">
    <:label>Name</:label>
    <:error :let={msg}>
      <.heroicon name="hero-exclamation-circle" class="icon" />
      {msg}
    </:error>
    <:edit_trigger><.heroicon name="hero-pencil-square" class="icon" /></:edit_trigger>
    <:submit_trigger><.heroicon name="hero-check" class="icon" /></:submit_trigger>
    <:cancel_trigger><.heroicon name="hero-x-mark" class="icon" /></:cancel_trigger>
  </.editable>
</.form>

Style

Use data attributes to target elements:

[data-scope="editable"][data-part="root"] {}
[data-scope="editable"][data-part="area"] {}
[data-scope="editable"][data-part="label"] {}
[data-scope="editable"][data-part="input"] {}
[data-scope="editable"][data-part="preview"] {}
[data-scope="editable"][data-part="edit-trigger"] {}
[data-scope="editable"][data-part="control"] {}
[data-scope="editable"][data-part="submit-trigger"] {}
[data-scope="editable"][data-part="cancel-trigger"] {}
[data-scope="editable"][data-part="error"] {}

If you wish to use the default Corex styling, you can use the class editable on the component. This requires the corex_design dependency and mix corex.design.build; import the component css file.

@import "../corex/corex.css";

You can then use modifiers

<.editable class="editable ui-accent ui-size-lg" value="">
  <:label>Label</:label>
  <:edit_trigger>Edit</:edit_trigger>
  <:submit_trigger>Save</:submit_trigger>
  <:cancel_trigger>Cancel</:cancel_trigger>
</.editable>

Axes: Semantic (ui-accent, ui-brand, ui-alert, ui-info, ui-success), Variant (ui-solid), Size (ui-size-smui-size-xl), Radius (ui-rounded-*). See the modifier guide.

Host width utilities (w-*, max-w-*, w-full) set the track the field fills. Preview and input share that width so entering edit mode stays stable (same pattern as clipboard).

Semantic modifiers set palette variables on the input, preview, and edit trigger. Variant modifiers control field surface treatment. Default is subtle; add editable ui-solid for a filled field. Cancel and submit triggers keep their alert and success styling.

Semantic

Palette variables for editable ink and fill. Does not change surface treatment by itself.

ModifierClasses
Defaulteditable
Accenteditable ui-accent
Brandeditable ui-brand
Alerteditable ui-alert
Infoeditable ui-info
Successeditable ui-success

Variant

Visual treatment of the input, preview, and edit trigger surfaces. Combine with a semantic modifier for palette-driven ink and fill.

ModifierClasses
Subtle (default)editable or editable ui-accent
Solideditable ui-accent ui-solid

Size

ModifierClasses
SMeditable ui-size-sm
MDeditable ui-size-md
LGeditable ui-size-lg
XLeditable ui-size-xl

Summary

API

Set the visible text value from a control (phx-click).

Set the value from handle_event.

Components

editable(assigns)

Attributes

  • id (:string) - The id of the editable component. Defaults to nil.
  • field (Phoenix.HTML.FormField) - A form field struct, e.g. f[:text] or @form[:text]. Defaults to nil.
  • name (:string) - The name attribute for form submission. Defaults to nil.
  • form (:string) - The id of the form this input belongs to. Defaults to nil.
  • invalid (:boolean) - Whether the control has validation errors. Defaults to nil.
  • auto_invalid (:boolean) - When true with field, set invalid from visible changeset errors (default false). Defaults to false.
  • disabled (:boolean) - Whether the control is disabled. Defaults to false.
  • read_only (:boolean) - Whether the editable is read-only. Defaults to false.
  • required (:boolean) - Whether the control is required. Defaults to false.
  • value (:string) - Initial preview text (Zag defaultValue). Defaults to "".
  • dir (:string) - Text direction. Defaults to nil. Must be one of nil, "ltr", or "rtl".
  • orientation (:string) - Defaults to "vertical". Must be one of "horizontal", or "vertical".
  • default_edit (:boolean) - Initial edit state. Defaults to false.
  • placeholder (:string) - Placeholder text when value is empty. Defaults to nil.
  • activation_mode (:string) - How to activate edit mode. Defaults to nil. Must be one of nil, "dblclick", or "focus".
  • select_on_focus (:boolean) - Whether to select all text on focus. Defaults to true.
  • on_value_change (:string) - Server event name when value changes. Defaults to nil.
  • on_value_change_client (:string) - Client event name when value changes. Defaults to nil.
  • translation (Corex.Editable.Translation) - Override translatable strings. Defaults to nil.
  • errors (:list) - List of error messages to display. Defaults to [].
  • Global attributes are accepted.

Slots

  • label (required) - Accepts attributes:
    • class (:string)
  • edit_trigger (required) - Accepts attributes:
    • class (:string)
  • submit_trigger (required) - Accepts attributes:
    • class (:string)
  • cancel_trigger (required) - Accepts attributes:
    • class (:string)
  • error - Accepts attributes:
    • class (:string)

API

set_value(editable_id, value)

@spec set_value(String.t(), String.t()) :: Phoenix.LiveView.JS.t()

Set the visible text value from a control (phx-click).

<.action phx-click={Corex.Editable.set_value("my-editable", "Hello")}>Reset</.action>
<.editable id="my-editable" class="editable" name="title" value="Hi">
  <:label>Title</:label>
  <:edit_trigger>Edit</:edit_trigger>
  <:submit_trigger>Save</:submit_trigger>
  <:cancel_trigger>Cancel</:cancel_trigger>
</.editable>
document.getElementById("my-editable")?.dispatchEvent(
  new CustomEvent("corex:editable:set-value", {
    bubbles: false,
    detail: { value: "Hello" },
  })
);

set_value(socket, editable_id, value)

Set the value from handle_event.

<.action phx-click="reset_title" phx-value-value="Hello">Reset</.action>
<.editable id="my-editable" class="editable" name="title" value="Hi">
  <:label>Title</:label>
  <:edit_trigger>Edit</:edit_trigger>
  <:submit_trigger>Save</:submit_trigger>
  <:cancel_trigger>Cancel</:cancel_trigger>
</.editable>
def handle_event("reset_title", %{"value" => v}, socket) do
  {:noreply, Corex.Editable.set_value(socket, "my-editable", v)}
end