PhiaUi.Components.Editor.TipTap (phia_ui v0.1.17)

Copy Markdown View Source

TipTap Editor Suite — 8 components for building ProseMirror-powered rich text editing experiences with Phoenix LiveView.

This module provides a real TipTap integration following the established PhiaUI pattern of optional npm dependencies (like PhiaChart with ECharts). When @tiptap/core is installed and exposed on window.tiptap, the full TipTap engine is used. Without it, the existing vanilla PhiaEditor (execCommand-based) provides a graceful fallback.

Architecture

  • JSON is canonical — TipTap stores documents as typed JSON trees
  • Server-driven toolbar — active marks/nodes pushed from client, toolbar state lives in LiveView assigns (not JS)
  • Form integration — hidden input syncs content for standard form submission
  • Phoenix eventson_update and on_selection event names for LiveView handlers

Components

Core

  • tiptap_editor/1 — main editor with toolbar, content area, form integration (PhiaTipTap)
  • tiptap_toolbar/1 — server-driven toolbar rendered from active marks/nodes assigns
  • tiptap_content/1 — read-only rendered content from TipTap JSON

Floating UI

Collaboration

Setup

# Install TipTap (optional — vanilla fallback without these)
npm install @tiptap/core @tiptap/starter-kit @tiptap/extension-placeholder

# In app.js:
import { Editor } from "@tiptap/core"
import StarterKit from "@tiptap/starter-kit"
import Placeholder from "@tiptap/extension-placeholder"
window.tiptap = { Editor, StarterKit, Placeholder }

# Register hooks:
import PhiaTipTap from "./hooks/tiptap_editor"
let liveSocket = new LiveSocket("/live", Socket, { hooks: { PhiaTipTap } })

Summary

Functions

Selection-aware floating toolbar for TipTap editor.

Collaborative cursor indicators for TipTap editor.

Renders TipTap JSON content as read-only HTML.

Main TipTap-powered rich text editor with toolbar, content area, and form integration.

Empty-line insertion menu for TipTap editor.

Mention suggestions dropdown for TipTap editor.

Slash command palette for TipTap editor.

Server-driven toolbar for TipTap editor.

Functions

tiptap_bubble_menu(assigns)

Selection-aware floating toolbar for TipTap editor.

Appears above text selection. When TipTap is installed, uses the native BubbleMenu extension. Otherwise uses the vanilla PhiaBubbleMenu positioning.

Example

<.tiptap_bubble_menu id="bubble-1" editor_id="my-editor">
  <button phx-click={...}>Bold</button>
</.tiptap_bubble_menu>

Attributes

  • id (:string) (required)
  • editor_id (:string) (required)
  • class (:string) - Defaults to nil.

Slots

  • inner_block (required)

tiptap_collab_cursors(assigns)

Collaborative cursor indicators for TipTap editor.

Renders colored cursor labels for connected users. Requires the TipTap Collaboration and CollaborationCursor extensions plus a Yjs transport (e.g. Phoenix Channel provider).

Example

<.tiptap_collab_cursors
  id="cursors-1"
  editor_id="my-editor"
  cursors={@connected_users}
/>

Each cursor should be a map with :name, :color, and optionally :avatar_url.

Attributes

  • id (:string) (required)
  • editor_id (:string) (required)
  • cursors (:list) - Defaults to [].
  • class (:string) - Defaults to nil.

tiptap_content(assigns)

Renders TipTap JSON content as read-only HTML.

Uses TipTapHelpers.content_to_html/1 for server-side JSON-to-HTML conversion. Always sanitize content before display.

Examples

<%!-- From JSON map --%>
<.tiptap_content content={@post.body_json} prose_size={:lg} />

<%!-- From JSON string --%>
<.tiptap_content content={Jason.decode!(@post.body)} />

Attributes

  • content (:any) - Defaults to nil.
  • prose_size (:atom) - Defaults to :base. Must be one of :sm, :base, or :lg.
  • class (:string) - Defaults to nil.
  • Global attributes are accepted.

tiptap_editor(assigns)

Main TipTap-powered rich text editor with toolbar, content area, and form integration.

Uses the PhiaTipTap hook which detects window.tiptap at runtime:

  • With TipTap: Full ProseMirror engine (JSON document model, transactions, undo/redo)
  • Without TipTap: Falls back to vanilla PhiaEditor (execCommand-based)

Content Flow

[Server: LiveView]
  assigns.content = JSON string
  push_event("editor:command:my-editor", %{command: "toggleBold"})
  |
[Client: PhiaTipTap hook]
  editor.getJSON()  pushEvent("editor:update", %{json, html, word_count})
  editor.isActive()  pushEvent("editor:selection", %{active_marks, ...})

Examples

<%!-- Standalone --%>
<.tiptap_editor id="blog-editor" placeholder="Write..." min_height="400px" />

<%!-- Form-integrated --%>
<.form for={@form} phx-submit="publish">
  <.tiptap_editor id="post-editor" field={@form[:body]} />
</.form>

<%!-- With server-driven commands --%>
def handle_event("make_bold", _, socket) do
  {:noreply, push_event(socket, "editor:command:post-editor", %{command: "toggleBold"})}
end

Attributes

  • id (:string) (required)
  • field (Phoenix.HTML.FormField) - Defaults to nil.
  • value (:string) - Defaults to nil.
  • placeholder (:string) - Defaults to "Type '/' for commands, or start writing...".
  • min_height (:string) - Defaults to "300px".
  • extensions (:list) - Defaults to [:starter_kit, :placeholder].
  • on_update (:string) - Defaults to "editor:update".
  • on_selection (:string) - Defaults to "editor:selection".
  • toolbar_variant (:atom) - Defaults to :default. Must be one of :default, :floating, or :compact.
  • show_word_count (:boolean) - Defaults to true.
  • show_find_replace (:boolean) - Defaults to false.
  • content_format (:atom) - Defaults to :json. Must be one of :json, or :html.
  • collab_enabled (:boolean) - Enable collaborative editing mode. Defaults to false.
  • collab_doc_id (:string) - Document ID for collaboration channel. Defaults to nil.
  • collab_user (:map) - Current user map with :id, :name, :color keys. Defaults to nil.
  • class (:string) - Defaults to nil.
  • Global attributes are accepted.

Slots

  • toolbar_extra - Extra toolbar content appended after default groups.

tiptap_floating_menu(assigns)

Empty-line insertion menu for TipTap editor.

Appears at the cursor when the current block is empty, offering block-level insertion options (heading, list, image, etc.).

Example

<.tiptap_floating_menu id="float-1" editor_id="my-editor">
  <button phx-click={...}>Heading</button>
  <button phx-click={...}>Image</button>
</.tiptap_floating_menu>

Attributes

  • id (:string) (required)
  • editor_id (:string) (required)
  • class (:string) - Defaults to nil.

Slots

  • inner_block (required)

tiptap_mention(assigns)

Mention suggestions dropdown for TipTap editor.

Triggered by typing "@". Shows a server-driven list of suggestions that the user can select to insert a mention node.

Example

<.tiptap_mention
  id="mention-1"
  editor_id="my-editor"
  suggestions={@mention_suggestions}
/>

Each suggestion should be a map with :id, :label, and optionally :avatar_url.

Attributes

  • id (:string) (required)
  • editor_id (:string) (required)
  • suggestions (:list) - Defaults to [].
  • class (:string) - Defaults to nil.

tiptap_slash_commands(assigns)

Slash command palette for TipTap editor.

Triggered by typing "/" at the start of a line. Renders a filterable list of block insertion commands. Commands dispatch to the editor via phx-click events.

Example

<.tiptap_slash_commands id="slash-1" editor_id="my-editor">
  <:command label="Heading 1" description="Large heading" action="setHeading" icon="heading" />
  <:command label="Bullet List" description="Unordered list" action="toggleBulletList" icon="list" />
</.tiptap_slash_commands>

Attributes

  • id (:string) (required)
  • editor_id (:string) (required)
  • class (:string) - Defaults to nil.

Slots

  • command - Accepts attributes:
    • label (:string) (required)
    • description (:string)
    • icon (:string)
    • action (:string) (required)

tiptap_toolbar(assigns)

Server-driven toolbar for TipTap editor.

Active states are determined by assigns (pushed from client via selection events), not by JavaScript DOM inspection. This keeps toolbar state in LiveView where it belongs.

Example

<.tiptap_toolbar
  editor_id="my-editor"
  active_marks={@editor_state.active_marks}
  active_nodes={@editor_state.active_nodes}
  heading_level={@editor_state.heading_level}
/>

Attributes

  • variant (:atom) - Defaults to :default. Must be one of :default, :floating, or :compact.
  • editor_id (:string) (required)
  • active_marks (:list) - Defaults to [].
  • active_nodes (:list) - Defaults to [].
  • heading_level (:any) - Defaults to nil.
  • class (:string) - Defaults to nil.

Slots

  • extra - Extra toolbar content appended after default groups.