PhiaUi.Components.Editable (phia_ui v0.1.17)

Copy Markdown View Source

Inline-editable field component — click to edit, Enter to submit, Escape to cancel.

Follows the shadcn/ui "Editable" pattern adapted for Phoenix LiveView. The component renders in two modes controlled entirely by the PhiaEditable JavaScript hook:

  • Preview mode — shows slot content (or placeholder). A click or keyboard activation switches to edit mode.
  • Edit mode — shows the :input slot (an <input> or <textarea>). Enter submits the value via pushEvent/3; Escape cancels without saving. Clicking outside the element also cancels.

Zero external JS dependencies — the hook is vanilla JS.

Usage

<.editable id="title-edit" value={@title} on_submit="update_title" placeholder="Click to edit">
  <:preview>{@title}</:preview>
  <:input>
    <input type="text" id="title-edit-input" name="title" value={@title}
           class="w-full border rounded px-2 py-1" />
  </:input>
</.editable>

In your LiveView, handle the submitted value:

def handle_event("update_title", %{"value" => value}, socket) do
  {:noreply, assign(socket, title: value)}
end

Attributes

AttributeTypeDefaultDescription
idstringrequiredUnique element ID (required by the hook)
valuestringnilCurrent value (for reference; not rendered)
placeholderstring"Click to edit"Text shown when preview slot is empty
on_submitstringnilLiveView event pushed when user submits
classstringnilExtra classes merged onto the container div

Slots

SlotRequiredDescription
previewyesContent rendered in preview (read) mode
inputyesContent rendered in edit mode (typically <input>)

Accessibility

  • The preview div has role="button" and tabindex="0" so keyboard users can activate it with Enter or Space.
  • aria-label="Click to edit" provides a descriptive accessible name.
  • The edit-mode input receives focus automatically on activation.

JS Hook: PhiaEditable

Register the hook exported from priv/templates/js/hooks/editable.js:

import PhiaEditable from "../../../deps/phia_ui/priv/templates/js/hooks/editable"

let liveSocket = new LiveSocket("/live", Socket, {
  hooks: { PhiaEditable }
})

Summary

Functions

Renders an inline-editable field.

Functions

editable(assigns)

Renders an inline-editable field.

The component shows the :preview slot by default. Clicking (or pressing Enter/Space) on the preview activates edit mode and shows the :input slot. The PhiaEditable JS hook manages the mode toggle, focus, keyboard handling, and event dispatch.

Example

<.editable id="bio-edit" value={@bio} on_submit="update_bio" placeholder="Add a bio…">
  <:preview>{@bio}</:preview>
  <:input>
    <textarea id="bio-edit-input" name="bio" rows="3"
              class="w-full border rounded px-2 py-1"><%= @bio %></textarea>
  </:input>
</.editable>

Attributes

  • id (:string) (required) - Unique element ID required by the PhiaEditable hook.
  • value (:string) - Current value of the field. Passed to the component for reference. Defaults to nil.
  • placeholder (:string) - Placeholder text shown when the preview slot renders no content. Defaults to "Click to edit".
  • on_submit (:string) - LiveView event name pushed when the user submits the new value. Omit to disable submission. Defaults to nil.
  • class (:string) - Additional CSS classes merged onto the container div via cn/1. Defaults to nil.

Slots

  • preview (required) - Content displayed in preview (read) mode. May be plain text or rich HTML.
  • input (required) - Content displayed in edit mode. Should contain an <input> or <textarea>.