Kotoba.Node behaviour (Kotoba v0.5.0)

Copy Markdown View Source

The behaviour of a node in a Kotoba.Document.

A node is a struct that mirrors one Lexical node in the serialized editor state. use Kotoba.Node makes the struct and the functions of the behaviour from a list of fields. The node module then gives the render callbacks:

defmodule MyApp.Nodes.Pointer do
  use Kotoba.Node, type: "pointer", kind: :inline

  import Phoenix.Component, only: [sigil_H: 2]

  field :ref, :string, required: true
  field :excerpt, :string, omit_nil: true
  field :tone, :string, key: "toneName", default: "plain", in: ~w(plain loud)

  @impl Kotoba.Node
  def render_html(node, _opts) do
    assigns = %{node: node}

    ~H"""
    <span class="pointer" data-ref={@node.ref} data-tone={@node.tone}>{@node.excerpt}</span>
    """
  end

  @impl Kotoba.Node
  def render_text(node, _opts), do: "[#{node.excerpt}]"
end

In the JSON, the tone field has the key "toneName". When excerpt is nil, to_json/1 does not write the "excerpt" key.

Options

  • :type - the Lexical node type, as in the "type" key of the JSON. Required.
  • :kind - :block, :inline or :decorator. Required.
  • :element - when true, the node is a Lexical element node. It gets a children list and the element fields direction, format and indent. The default is false.

Every node gets a version field (default 1) and an extra map. The extra map keeps the JSON keys that the node does not declare, so to_json/1 writes them back with no change.

Fields

field/3 declares one attribute. The types are :string, :integer, :boolean, :map and {:array, type}. The options are:

  • :key - the JSON key. The default is the field name as a string. Use it for the camelCase keys of Lexical, for example field :list_type, :string, key: "listType".
  • :required - when true, the value must not be nil.
  • :default - the struct default. from_json/1 also uses it when the JSON has no value for the key.
  • :in - a list of the permitted values.
  • :omit_nil - when true, to_json/1 does not write the key when the value is nil. When false (the default), it writes null.

The field names :children, :extra, :type and :version are reserved. The JSON keys "type", "children" and "version" are reserved too, and two fields cannot have the same JSON key. A module that breaks one of these rules does not compile.

Generated functions

use Kotoba.Node defines type/0, kind/0, element?/0, fields/0, validate/1, from_json/1, to_json/1 and a default render_markdown/2. A node can override validate/1 to add its own checks, and call super/1 for the field checks. It can override render_markdown/2; the default calls render_text/2.

from_json/1 reads the attributes of one node. It does not read the children: Kotoba.Document.parse/2 reads them with the node registry.

Rendering

Kotoba.Renderer calls render_html/2, render_text/2 and render_markdown/2 for each node, after Kotoba.Sanitizer accepts the node. A node module must give render_html/2 and render_text/2.

render_html/2 returns safe HTML: {:safe, iodata} from Phoenix.HTML, or a ~H template. A plain string is escaped. An element node renders its children with Kotoba.Renderer.html_children/2, Kotoba.Renderer.text_children/2 and Kotoba.Renderer.markdown_children/2. The opts are described in Kotoba.Renderer.

Summary

Types

The kind of a node.

t()

A node struct.

Callbacks

Returns true when the node is a Lexical element node with children.

Returns the fields of the node, in the order of declaration.

Builds the node from its JSON map. The children are not read.

Returns the kind of the node.

Renders the node as safe HTML, or as a ~H template.

Renders the node as Markdown.

Renders the node as plain text.

Returns the JSON map of the node, with the JSON of its children.

Returns the Lexical node type.

Checks the node. Returns :ok or {:error, messages}.

Functions

Returns the JSON map of any node, Kotoba.Nodes.Unknown included.

Declares a field of the node. See Kotoba.Node.Field for the options.

Builds a node struct of module from a JSON map.

Returns true when module is a module that uses Kotoba.Node.

Returns the JSON map of a node struct, with the JSON of its children.

Checks every field of a node struct against its declaration.

Types

kind()

@type kind() :: :block | :inline | :decorator

The kind of a node.

t()

@type t() :: struct()

A node struct.

Callbacks

element?()

@callback element?() :: boolean()

Returns true when the node is a Lexical element node with children.

fields()

@callback fields() :: [Kotoba.Node.Field.t()]

Returns the fields of the node, in the order of declaration.

from_json(json)

@callback from_json(json :: map()) :: t()

Builds the node from its JSON map. The children are not read.

kind()

@callback kind() :: kind()

Returns the kind of the node.

render_html(node, opts)

@callback render_html(node :: t(), opts :: keyword()) ::
  Phoenix.HTML.safe() | Phoenix.LiveView.Rendered.t()

Renders the node as safe HTML, or as a ~H template.

render_markdown(node, opts)

@callback render_markdown(node :: t(), opts :: keyword()) :: String.t()

Renders the node as Markdown.

render_text(node, opts)

@callback render_text(node :: t(), opts :: keyword()) :: String.t()

Renders the node as plain text.

to_json(node)

@callback to_json(node :: t()) :: map()

Returns the JSON map of the node, with the JSON of its children.

type()

@callback type() :: String.t()

Returns the Lexical node type.

validate(node)

@callback validate(node :: t()) :: :ok | {:error, [String.t()]}

Checks the node. Returns :ok or {:error, messages}.

Functions

encode(node)

@spec encode(t()) :: map()

Returns the JSON map of any node, Kotoba.Nodes.Unknown included.

field(name, type, opts \\ [])

(macro)

Declares a field of the node. See Kotoba.Node.Field for the options.

from_json(module, json)

@spec from_json(module(), map()) :: t()

Builds a node struct of module from a JSON map.

The generated from_json/1 calls this function. A declared key that is not in the map gets the field default. The keys that the node does not declare go into extra. For an element node, the "children" key is not read and the struct gets no children.

node_module?(module)

@spec node_module?(module()) :: boolean()

Returns true when module is a module that uses Kotoba.Node.

to_json(node)

@spec to_json(t()) :: map()

Returns the JSON map of a node struct, with the JSON of its children.

The generated to_json/1 calls this function.

validate_fields(node)

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

Checks every field of a node struct against its declaration.

The generated validate/1 calls this function.