# `A2UIAgent.Builder`
[🔗](https://github.com/a2ui-elixir/a2ui/blob/v0.1.1/umbrella/apps/a2ui_agent/lib/a2ui_agent/builder.ex#L1)

Builds component trees in Elixir and flattens them into the component
lists that A2UI messages carry.

A tree is made of component nodes (`component/2`, or the functions of
`A2UIAgent.Builder.Basic`). A node placed in a property of another
node becomes a component of its own, and the property holds its id:

    iex> import A2UIAgent.Builder
    iex> import A2UIAgent.Builder.Basic
    iex> tree =
    ...>   card(
    ...>     id: "account",
    ...>     child:
    ...>       column(
    ...>         children: [
    ...>           text(text: "Account overview"),
    ...>           button(child: text(text: "Details"), action: event("details"))
    ...>         ]
    ...>       )
    ...>   )
    iex> tree |> flatten() |> Enum.map(&{&1["id"], &1["component"]})
    [
      {"account__text_1", "Text"},
      {"account__text_2", "Text"},
      {"account__button_1", "Button"},
      {"account__column_1", "Column"},
      {"account", "Card"}
    ]

Children come before their parents, so every reference points at a
component already in the list.

## Ids

The root's id is the `:root_id` option, else its own `:id`. The other
components are named after the root: a node with an `:id` gets
`<root>__<id>`, and a node without one gets `<root>__<name>_<n>`, its
component name in lower case and a count per name, skipping ids the
author gave. A root without an id, and no `:root_id`, takes such an id
under the prefix `root`. A list of trees is flattened tree by tree;
with a `:root_id`, the trees' roots are `<root_id>_0`, `<root_id>_1`
and so on.

## Values

Properties are given as a keyword list or a map. Atom keys are written
in lower camel case (`validation_regexp:` becomes
`"validationRegexp"`); string keys are written as they are. Besides
plain JSON values and nodes, a property can hold:

  * `ref/1`, a component already on the surface, written as its id and
    never emitted;
  * `child_list/2`, children repeated over a data model list;
  * `bind/1`, a data binding;
  * `call/3`, a function call;
  * `event/2` and `action/1`, the two kinds of action;
  * `check/2`, a check rule, and `accessibility/1`, accessibility
    attributes.

Bindings and function calls are written with the keys of the protocol
version (`path` and `call` before v1.0, `@path` and `@call` from it).
`nil` properties are left out.

## Catalogs

With the `:catalog` option, `flatten/2` completes each component with
the defaults its catalog schema declares for properties the node does
not set, emits children in the order the catalog declares the
properties holding them, and checks each component against the catalog
(`A2UICore.Validation.Payload.validate_component/3`), raising
`A2UICore.Error` for one the catalog rejects. This is how the
reference SDKs' generated builders behave.

Macros (`A2UIAgent.Macro`) can be called inside a tree; see
`A2UIAgent.Macro.call/2`.

# `props`
*since 0.1.0* 

```elixir
@type props() :: keyword() | map()
```

Properties: a keyword list or a map.

# `tree`
*since 0.1.0* 

```elixir
@type tree() ::
  A2UIAgent.Builder.Component.t()
  | A2UIAgent.Builder.Ref.t()
  | A2UIAgent.Macro.Call.t()
  | [tree()]
```

A component tree, or a list of them.

# `accessibility`
*since 0.1.0* 

```elixir
@spec accessibility(props()) :: map()
```

Accessibility attributes, such as `label:` and `description:`.

    iex> A2UIAgent.Builder.accessibility(label: "Close")
    %{"label" => "Close"}

# `action`
*since 0.1.0* 

```elixir
@spec action(A2UIAgent.Builder.FunctionCall.t()) :: map()
```

An action that calls a client function (see `call/3`).

# `bind`
*since 0.1.0* 

```elixir
@spec bind(String.t()) :: A2UIAgent.Builder.Binding.t()
```

A data binding. An absolute path (`/user/name`) reads from the data
model's root; a relative one (`name`) from the enclosing scope, such
as a `child_list/2` item.

# `call`
*since 0.1.0* 

```elixir
@spec call(String.t(), props(), keyword()) :: A2UIAgent.Builder.FunctionCall.t()
```

A call of the catalog function `name`. `args` follow the key rules of
properties. Options: `:return_type`, written as `returnType`, and
`:catalog_id`, the catalog the call runs in when it is not the
surface's default, written as `catalogId` from protocol v1.0 (before
it a call always runs in the surface's catalog, and the option is left
out).

    iex> import A2UIAgent.Builder.Basic
    iex> text(id: "root", text: A2UIAgent.Builder.call("shout", [value: "hi"], catalog_id: "https://example.com/c.json"))
    ...> |> A2UIAgent.Builder.flatten()
    [
      %{
        "id" => "root",
        "component" => "Text",
        "text" => %{"@call" => "shout", "args" => %{"value" => "hi"}, "catalogId" => "https://example.com/c.json"}
      }
    ]

# `check`
*since 0.1.0* 

```elixir
@spec check(term(), String.t()) :: map()
```

A check rule: `condition` (usually a `call/3`) and the message shown when it fails.

# `child_list`
*since 0.1.0* 

```elixir
@spec child_list(
  String.t(),
  A2UIAgent.Builder.Component.t() | A2UIAgent.Builder.Ref.t()
) ::
  A2UIAgent.Builder.ChildList.t()
```

Children generated from the data model list at `path`: `template` is
emitted once and repeated for each item.

# `component`
*since 0.1.0* 

```elixir
@spec component(String.t(), props()) :: A2UIAgent.Builder.Component.t()
```

A component node. `props` may set `:id`; see the moduledoc for keys and
values.

    iex> A2UIAgent.Builder.component("Text", id: "title", text: "Hello").props
    [{"text", "Hello"}]

# `event`
*since 0.1.0* 

```elixir
@spec event(String.t(), props() | nil) :: map()
```

An action that sends the event `name` to the agent, with an optional
`context` (keys written as given).

    iex> A2UIAgent.Builder.event("save", order: "o-1")
    %{"event" => %{"name" => "save", "context" => %{"order" => "o-1"}}}

# `flatten`
*since 0.1.0* 

```elixir
@spec flatten(tree(), keyword()) :: [map()]
```

Flattens `tree` into a list of component maps, children first.

Options:

  * `:root_id` — the root's id, and the prefix of the ids made for the
    components under it;
  * `:catalog` — complete and check components against this catalog
    (see "Catalogs" above);
  * `:validate` — with a catalog, whether to check components (default
    `true`);
  * `:version` — the protocol version whose keys bindings and function
    calls are written with (default: the catalog's, else `"v1.0"`);
  * `:macros` — the macros (`A2UIAgent.Macro`) that macro calls in the
    tree may name.

Raises `ArgumentError` for a value that cannot be written as JSON and
`A2UICore.Error` for a component the catalog rejects, or for macro
calls nested deeper than 16.

# `messages`
*since 0.1.0* 

```elixir
@spec messages(tree(), keyword()) :: [map()]
```

The messages that put `tree` on a surface: a `createSurface` when
`:catalog_id` is given, an `updateComponents` with the flattened
components, and an `updateDataModel` for each `:data_model` entry.

Options: `:surface_id` (required), `:catalog_id`, `:data_model` (path
and value pairs, or a map), `:version` (the version messages state;
default: the catalog's, else `"v1.0"`), and the options of
`flatten/2`.

    iex> import A2UIAgent.Builder.Basic
    iex> A2UIAgent.Builder.messages(text(id: "root", text: "Hi"), surface_id: "s", version: "v0.9")
    [
      %{
        "version" => "v0.9",
        "updateComponents" => %{
          "surfaceId" => "s",
          "components" => [%{"id" => "root", "component" => "Text", "text" => "Hi"}]
        }
      }
    ]

# `ref`
*since 0.1.0* 

```elixir
@spec ref(String.t()) :: A2UIAgent.Builder.Ref.t()
```

A reference to a component already on the surface, by id.

---

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