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

Copy Markdown View Source

Pure Elixir utilities for working with editor JSON content.

Documents are stored as a JSON tree of typed nodes with marks. This module provides server-side operations — HTML rendering, plain-text extraction, validation, and sanitization — so you can process editor content without JavaScript.

Editor JSON Format

%{
  "type" => "doc",
  "content" => [
    %{
      "type" => "paragraph",
      "content" => [
        %{"type" => "text", "text" => "Hello ", "marks" => [%{"type" => "bold"}]},
        %{"type" => "text", "text" => "world"}
      ]
    }
  ]
}

Usage

alias PhiaUi.Components.Editor.EditorContent

# Render JSON to safe HTML
html = EditorContent.content_to_html(json)

# Extract plain text for search indexing
text = EditorContent.content_to_text(json)

# Validate against allowed schema
{:ok, json} = EditorContent.validate_content(json, allowed_nodes: ~w(paragraph heading))

# Count words
count = EditorContent.word_count(json)

Summary

Functions

Converts an editor JSON document to an HTML string.

Extracts plain text from an editor JSON document.

Returns the canonical empty document.

Strips disallowed nodes and marks from an editor JSON document.

Validates an editor JSON document against allowed node and mark types.

Counts words in an editor JSON document.

Functions

content_to_html(arg1)

@spec content_to_html(map() | nil) :: String.t()

Converts an editor JSON document to an HTML string.

The output is not marked as safe — use Phoenix.HTML.raw/1 when rendering in templates, after sanitizing user content.

Examples

iex> doc = %{"type" => "doc", "content" => [
...>   %{"type" => "paragraph", "content" => [
...>     %{"type" => "text", "text" => "Hello"}
...>   ]}
...> ]}
iex> EditorContent.content_to_html(doc)
"<p>Hello</p>"

content_to_text(arg1)

@spec content_to_text(map() | nil) :: String.t()

Extracts plain text from an editor JSON document.

Block-level nodes are separated by newlines. Useful for search indexing, word counting, and preview generation.

Examples

iex> doc = %{"type" => "doc", "content" => [
...>   %{"type" => "paragraph", "content" => [
...>     %{"type" => "text", "text" => "Hello world"}
...>   ]},
...>   %{"type" => "paragraph", "content" => [
...>     %{"type" => "text", "text" => "Second paragraph"}
...>   ]}
...> ]}
iex> EditorContent.content_to_text(doc)
"Hello world\nSecond paragraph"

empty_document()

@spec empty_document() :: map()

Returns the canonical empty document.

Example

iex> EditorContent.empty_document()
%{"type" => "doc", "content" => [%{"type" => "paragraph"}]}

sanitize_content(doc, opts \\ [])

@spec sanitize_content(
  map(),
  keyword()
) :: map()

Strips disallowed nodes and marks from an editor JSON document.

Nodes with disallowed types are removed entirely. Marks with disallowed types are stripped from text nodes (text is kept). Returns the cleaned document.

Options

  • :allowed_nodes — list of allowed node type strings
  • :allowed_marks — list of allowed mark type strings

Examples

iex> doc = %{"type" => "doc", "content" => [
...>   %{"type" => "paragraph", "content" => [%{"type" => "text", "text" => "ok"}]},
...>   %{"type" => "script", "content" => []}
...> ]}
iex> EditorContent.sanitize_content(doc)
%{"type" => "doc", "content" => [
  %{"type" => "paragraph", "content" => [%{"type" => "text", "text" => "ok"}]}
]}

validate_content(doc, opts \\ [])

@spec validate_content(
  map(),
  keyword()
) :: {:ok, map()} | {:error, [String.t()]}

Validates an editor JSON document against allowed node and mark types.

Returns {:ok, doc} if valid, or {:error, reasons} with a list of issues.

Options

  • :allowed_nodes — list of allowed node type strings (default: standard set)
  • :allowed_marks — list of allowed mark type strings (default: standard set)

Examples

iex> doc = %{"type" => "doc", "content" => [%{"type" => "paragraph"}]}
iex> EditorContent.validate_content(doc)
{:ok, doc}

iex> doc = %{"type" => "doc", "content" => [%{"type" => "script"}]}
iex> EditorContent.validate_content(doc)
{:error, ["disallowed node type: script"]}

word_count(doc)

@spec word_count(map() | nil) :: non_neg_integer()

Counts words in an editor JSON document.

Examples

iex> doc = %{"type" => "doc", "content" => [
...>   %{"type" => "paragraph", "content" => [
...>     %{"type" => "text", "text" => "Hello beautiful world"}
...>   ]}
...> ]}
iex> EditorContent.word_count(doc)
3