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/2andaction/1, the two kinds of action;check/2, a check rule, andaccessibility/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.
Summary
Functions
Accessibility attributes, such as label: and description:.
An action that calls a client function (see call/3).
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.
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).
A check rule: condition (usually a call/3) and the message shown when it fails.
Children generated from the data model list at path: template is
emitted once and repeated for each item.
A component node. props may set :id; see the moduledoc for keys and
values.
An action that sends the event name to the agent, with an optional
context (keys written as given).
Flattens tree into a list of component maps, children first.
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.
A reference to a component already on the surface, by id.
Types
Properties: a keyword list or a map.
@type tree() :: A2UIAgent.Builder.Component.t() | A2UIAgent.Builder.Ref.t() | A2UIAgent.Macro.Call.t() | [tree()]
A component tree, or a list of them.
Functions
Accessibility attributes, such as label: and description:.
iex> A2UIAgent.Builder.accessibility(label: "Close")
%{"label" => "Close"}
@spec action(A2UIAgent.Builder.FunctionCall.t()) :: map()
An action that calls a client function (see call/3).
@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.
@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"}
}
]
A check rule: condition (usually a call/3) and the message shown when it fails.
@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.
@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"}]
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"}}}
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 (defaulttrue);: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.
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"}]
}
}
]
@spec ref(String.t()) :: A2UIAgent.Builder.Ref.t()
A reference to a component already on the surface, by id.