ElementorexCms.Document (ElementorexCms v0.1.0)

Copy Markdown View Source

The document: a tree of nodes stored as plain JSON (a jsonb column).

%{
  "version" => 1,
  "root" => %{
    "id" => "Wq0m2Lx9Ka",
    "type" => "root",
    "props" => %{},
    "style" => %{},
    "attrs" => %{"css" => "selector { padding: 8px }"},
    "children" => [
      %{
        "id" => "b3Rt9Lm1Qe",
        "type" => "heading",
        "props" => %{"text" => "Hello", "level" => "h2"},
        "style" => %{"desktop" => %{"color" => "var(--color-primary)"}, "mobile" => %{"font-size" => "28px"}},
        "attrs" => %{"class" => "tracking-tight", "hide_on" => ["mobile"]},
        "children" => []
      }
    ]
  }
}

Every node has:

  • "id" - stable id (ElementorexCms.Id)
  • "type" - the block type (ElementorexCms.Registry)
  • "props" - content of the block, as declared by its props
  • "style" - declarations per state: "desktop", "tablet", "mobile" and "hover" (see ElementorexCms.Style)
  • "attrs" - Advanced settings: "id", "class", "hide_on", "css", "animation", "animation_delay" and "label" (name in the tree)
  • "children" - child nodes (containers only)

The functions here are pure: they take a document and return a new one. Tree operations validate what they are asked (a node cannot go inside itself, only containers take children, the root cannot be removed) and return {:error, reason} otherwise.

Summary

Functions

Entrance animations of the Advanced tab.

Removes every style of a node (all states).

Number of nodes, the root included.

Devices of the responsive styles.

Copies the node id (with new ids for it and its children) right after it. Returns the document and the id of the copy.

Whether the document has no content.

Like get/2, as {:ok, node} or {:error, :not_found}.

Decodes and normalizes a JSON document (an invalid one gives new/0).

The node with id, or nil.

Inserts node in the container parent_id at index (clamped to the children list). Checks that the parent takes that kind of child and the document limits.

Limits applied by normalize/2: %{nodes: 2000, depth: 24}.

Moves the node id into parent_id at index. The index is counted in the target's current children (the node itself included when it already is there), which is what the editor's drop indicator shows.

An empty document, optionally with root children.

A new node of type. props are stored as given (string keys); pass registry: to fill in the defaults of the block and its default children.

Every node, depth first, the root included.

Makes any map a valid document for registry: fills missing keys, casts and sanitizes props with the definitions of each block, keeps only valid styles and Advanced attributes, drops children of non-containers, gives new ids to invalid or repeated ones and enforces the limits (limits/0).

Keeps only valid Advanced attributes.

Keeps only valid states and declarations of a style map.

The parent of the node with id and the node's index in it.

Nodes from the root down to the node with id (both included), or [] if it does not exist.

Sets an Advanced attribute of a node (validated with normalize_attrs/1; an invalid value removes the attribute).

Sets a prop of a node (the value is stored as given; cast it first).

Sets (or with nil/"" removes) a style declaration of a node for a state ("desktop", "tablet", "mobile" or "hover"). Invalid values are rejected with {:error, :invalid}.

A copy of a node (or of a whole document) with new ids everywhere.

Removes the node id and its children. The root cannot be removed.

The root node.

Style states a node can have (desktop, tablet, mobile, hover).

Encodes a document as JSON.

Applies fun to the node with id. The id and type cannot change.

Current version of the document format.

Whether id is ancestor_id or one of its descendants.

Types

t()

@type t() :: map()

tree_node()

@type tree_node() :: map()

Functions

animations()

Entrance animations of the Advanced tab.

clear_style(doc, id)

@spec clear_style(t(), String.t()) :: {:ok, t()} | {:error, :not_found}

Removes every style of a node (all states).

count(doc)

@spec count(t() | tree_node()) :: non_neg_integer()

Number of nodes, the root included.

devices()

Devices of the responsive styles.

duplicate(doc, id, registry \\ nil)

@spec duplicate(t(), String.t(), ElementorexCms.Registry.t() | nil) ::
  {:ok, t(), String.t()} | {:error, atom()}

Copies the node id (with new ids for it and its children) right after it. Returns the document and the id of the copy.

empty?(arg1)

@spec empty?(t() | nil) :: boolean()

Whether the document has no content.

fetch(doc, id)

@spec fetch(t(), String.t() | nil) :: {:ok, tree_node()} | {:error, :not_found}

Like get/2, as {:ok, node} or {:error, :not_found}.

from_json(json, registry \\ nil)

@spec from_json(String.t(), ElementorexCms.Registry.t() | nil) :: t()

Decodes and normalizes a JSON document (an invalid one gives new/0).

get(arg1, id)

@spec get(t(), String.t() | nil) :: tree_node() | nil

The node with id, or nil.

insert(doc, node, parent_id, index, registry \\ nil)

@spec insert(
  t(),
  tree_node(),
  String.t(),
  integer(),
  ElementorexCms.Registry.t() | nil
) ::
  {:ok, t()} | {:error, atom()}

Inserts node in the container parent_id at index (clamped to the children list). Checks that the parent takes that kind of child and the document limits.

limits()

Limits applied by normalize/2: %{nodes: 2000, depth: 24}.

move(doc, id, parent_id, index, registry \\ nil)

@spec move(t(), String.t(), String.t(), integer(), ElementorexCms.Registry.t() | nil) ::
  {:ok, t()} | {:error, atom()}

Moves the node id into parent_id at index. The index is counted in the target's current children (the node itself included when it already is there), which is what the editor's drop indicator shows.

new(children \\ [])

@spec new([tree_node()]) :: t()

An empty document, optionally with root children.

node(type, props \\ %{}, opts \\ [])

@spec node(String.t(), map(), keyword()) :: tree_node()

A new node of type. props are stored as given (string keys); pass registry: to fill in the defaults of the block and its default children.

Options: :children, :style, :attrs, :id, :registry.

nodes(node)

@spec nodes(t() | tree_node()) :: [tree_node()]

Every node, depth first, the root included.

normalize(doc, registry \\ nil)

@spec normalize(term(), ElementorexCms.Registry.t() | nil) :: t()

Makes any map a valid document for registry: fills missing keys, casts and sanitizes props with the definitions of each block, keeps only valid styles and Advanced attributes, drops children of non-containers, gives new ids to invalid or repeated ones and enforces the limits (limits/0).

Nodes of unknown types are kept as they are (they render nothing), so a document never loses content because a block was removed from the registry.

Run it on everything that comes from outside: stored JSON, imports, the editor's events.

normalize_attrs(attrs)

@spec normalize_attrs(term()) :: map()

Keeps only valid Advanced attributes.

normalize_style(style)

@spec normalize_style(term()) :: map()

Keeps only valid states and declarations of a style map.

parent(doc, id)

@spec parent(t(), String.t()) :: {:ok, tree_node(), non_neg_integer()} | :error

The parent of the node with id and the node's index in it.

path(map, id)

@spec path(t(), String.t()) :: [tree_node()]

Nodes from the root down to the node with id (both included), or [] if it does not exist.

put_attr(doc, id, key, value)

@spec put_attr(t(), String.t(), String.t(), term()) ::
  {:ok, t()} | {:error, :not_found}

Sets an Advanced attribute of a node (validated with normalize_attrs/1; an invalid value removes the attribute).

put_prop(doc, id, key, value)

@spec put_prop(t(), String.t(), String.t(), term()) ::
  {:ok, t()} | {:error, :not_found}

Sets a prop of a node (the value is stored as given; cast it first).

put_style(doc, id, state, property, value)

@spec put_style(t(), String.t(), String.t(), String.t(), term()) ::
  {:ok, t()} | {:error, :not_found | :invalid}

Sets (or with nil/"" removes) a style declaration of a node for a state ("desktop", "tablet", "mobile" or "hover"). Invalid values are rejected with {:error, :invalid}.

regenerate_ids(doc)

@spec regenerate_ids(t() | tree_node()) :: t() | tree_node()

A copy of a node (or of a whole document) with new ids everywhere.

remove(doc, id)

@spec remove(t(), String.t()) :: {:ok, t()} | {:error, atom()}

Removes the node id and its children. The root cannot be removed.

root(map)

@spec root(t()) :: tree_node()

The root node.

states()

Style states a node can have (desktop, tablet, mobile, hover).

to_json(doc, opts \\ [])

@spec to_json(t(), keyword()) :: String.t()

Encodes a document as JSON.

update(doc, id, fun)

@spec update(t(), String.t(), (tree_node() -> tree_node())) ::
  {:ok, t()} | {:error, :not_found}

Applies fun to the node with id. The id and type cannot change.

version()

Current version of the document format.

within?(doc, id, ancestor_id)

@spec within?(t(), String.t(), String.t()) :: boolean()

Whether id is ancestor_id or one of its descendants.