# `PhiaUi.Components.Editor.TipTap`
[🔗](https://github.com/charlenopires/PhiaUI/blob/v0.1.17/lib/phia_ui/components/editor/tiptap.ex#L1)

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 events** — `on_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
- `tiptap_bubble_menu/1`   — selection-aware floating toolbar (PhiaTipTap)
- `tiptap_floating_menu/1` — empty-line insertion menu (PhiaTipTap)
- `tiptap_slash_commands/1` — "/" command palette (PhiaTipTap)
- `tiptap_mention/1`       — @ mention suggestions (PhiaTipTap)

### Collaboration
- `tiptap_collab_cursors/1` — collaborative cursor indicators (PhiaTipTapCollab)

## 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 } })

# `tiptap_bubble_menu`

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`

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`

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`

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`

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`

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`

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`

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.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
