defmodule <%= @module_name %>.Components.UI.RichTextEditor do @moduledoc """ Rich Text Editor Component for Phoenix LiveView. Ejected from PhiaUI — owns this copy of the source. Customise freely. Provides `rich_text_editor/1` — a contenteditable-based rich text editor with a full toolbar, Phoenix.HTML.Form integration, and changeset error display. Powered by the `PhiaRichTextEditor` JS hook (zero npm dependencies). ## Registration Register the hook in `app.js`: import PhiaRichTextEditor from "./phia_hooks/rich_text_editor.js" let liveSocket = new LiveSocket("/live", Socket, { hooks: { PhiaRichTextEditor } }) ## Example <.rich_text_editor field={@form[:body]} label="Content" placeholder="Write something..." /> ## Error handling Errors are read directly from `field.errors`. For Gettext translations, replace `translate_error/1` with: defp translate_error({msg, opts}) do if count = opts[:count] do Gettext.dngettext(<%= @module_name %>.Gettext, "errors", msg, msg, count, opts) else Gettext.dgettext(<%= @module_name %>.Gettext, "errors", msg, opts) end end """ use Phoenix.Component # --------------------------------------------------------------------------- # rich_text_editor/1 # --------------------------------------------------------------------------- attr :field, Phoenix.HTML.FormField, required: true, doc: "A `Phoenix.HTML.FormField` struct (e.g., `@form[:body]`)" attr :label, :string, default: nil, doc: "Label text rendered above the editor" attr :placeholder, :string, default: nil, doc: "Placeholder text shown when the editor is empty" attr :min_height, :string, default: "200px", doc: "Minimum height of the editable area (CSS value)" attr :class, :string, default: nil, doc: "Additional CSS classes for the outer wrapper" def rich_text_editor(assigns) do assigns = assign(assigns, :errors, Enum.map(assigns.field.errors, &translate_error/1)) ~H"""
<%!-- Toolbar --%>
<%!-- Group 1: Inline marks --%> <%!-- Group 2: Headings + paragraph --%> <%!-- Group 3: Lists --%> <%!-- Group 4: Blockquote + code --%> <%!-- Group 5: Link --%>
<%!-- Editable area --%>
<%!-- Hidden input for changeset integration --%> <%!-- Changeset errors --%>

{error}

""" end defp translate_error({msg, opts}) do Enum.reduce(opts, msg, fn {key, value}, acc when is_binary(acc) -> String.replace(acc, "%{#{key}}", to_string(value)) _other, acc -> acc end) end end