# `Kotoba.Node`
[🔗](https://github.com/hattoriai/kotoba/blob/v0.5.0/lib/kotoba/node.ex#L1)

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 `c:render_html/2`, `c:render_text/2` and
`c:render_markdown/2` for each node, after `Kotoba.Sanitizer` accepts the
node. A node module must give `c:render_html/2` and `c:render_text/2`.

`c: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`.

# `kind`

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

The kind of a node.

# `t`

```elixir
@type t() :: struct()
```

A node struct.

# `element?`

```elixir
@callback element?() :: boolean()
```

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

# `fields`

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

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

# `from_json`

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

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

# `kind`

```elixir
@callback kind() :: kind()
```

Returns the kind of the node.

# `render_html`

```elixir
@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`

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

Renders the node as Markdown.

# `render_text`

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

Renders the node as plain text.

# `to_json`

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

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

# `type`

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

Returns the Lexical node type.

# `validate`

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

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

# `encode`

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

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

# `field`
*macro* 

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

# `from_json`

```elixir
@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?`

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

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

# `to_json`

```elixir
@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`

```elixir
@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.

---

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