# `Macaw.Surface`
[🔗](https://github.com/dcoai/macaw/blob/v0.1.3/lib/macaw/surface.ex#L1)

A surface: a reusable composed unit of cells in a 12-column grid (spec/06 §3.1).

    {:ok, surface} =
      Macaw.Surface.new(
        name: "service_overview",
        title: "Service overview",
        cells: %{
          "latency" => %{component: "macaw.table", place: %{x: 0, y: 0, w: 6, h: 4},
                         slots: %{"rows" => %{source: "latency"}}}
        }
      )

A surface is a document, read through the kernel's gates and stacked as a fragment:

  * the map form is `%{version: v, surface: node}` (`to_map/1`), the JSON form
    `{"$version": v, "surface": {…}}` (`to_json/1`); `from_map/2` and `from_json/2` read
    them back through every gate (the version, the limits, the migration), then `new/1`;
  * as a fragment type, `cells` is a `:union` by name and every other key an `:override`,
    so a layer adds or replaces cells by name and overrides the title.

Names are strings, an atom read as its spelling; no atom is made from a document.

# `t`

```elixir
@type t() :: %Macaw.Surface{
  cells: %{required(String.t()) =&gt; Macaw.Surface.Cell.t()},
  layout: :grid,
  name: String.t(),
  title: String.t() | nil
}
```

# `fetch`

```elixir
@spec fetch(Macaw.Surface.Validated.t(), keyword()) :: %{
  required(String.t()) =&gt; map()
}
```

Each cell's deliveries and drawn state (spec/06 §3.5): `%{cell => %{deliveries:, drawn:}}`.
`env`: `sources:` (as given to `validate/3`), `now:` and `ceiling:` (required),
`viewer:` (`nil`), `width:` (`1200`), `row_height:` (`64`), `timeout:` (`5_000` ms).
Each fetch runs bounded in a process of its own, so it never blocks or crashes the caller.

# `fragment`

```elixir
@spec fragment(t()) :: %{surface: map()}
```

`%{surface: node}`: the surface as a fragment.

# `from_json`

```elixir
@spec from_json(String.t(), keyword()) ::
  {:ok, t()} | {:error, [Macaw.Layers.Fault.t(), ...]}
```

The surface a JSON document holds, read as `from_map/2` reads a map, every gate first.

# `from_map`

```elixir
@spec from_map(term(), keyword()) ::
  {:ok, t()} | {:error, [Macaw.Layers.Fault.t(), ...]}
```

The surface a map document holds, or every fault: the version gate first, then the
limits (`limits:` as `Macaw.Layers.Document.read/3` takes them) and the migration, then
`new/1`.

# `new`

```elixir
@spec new(keyword() | map()) :: {:ok, t()} | {:error, [Macaw.Layers.Fault.t(), ...]}
```

A surface from a keyword list or a map with atom keys: `{:ok, surface}` or every fault
at once, by path from the surface, in the kernel's order.

# `node`

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

The surface as its node: its keys, each cell as its map, names that spell one of the
document's keys written as that key.

# `render`

```elixir
@spec render(Macaw.Surface.Validated.t(), keyword()) :: Phoenix.HTML.safe()
```

Fetches and renders in one call: `render(validated, fetch(validated, env), env)`.

# `render`

```elixir
@spec render(Macaw.Surface.Validated.t(), map(), keyword()) :: Phoenix.HTML.safe()
```

The surface's markup (spec/06 §3.6), a safe value: its title at `heading_level:` (`2`),
then its cells in reading order, in `mode:` (`:socketless`), with `theme:`, `now:`,
`width:` and `row_height:` as for fetching.

# `to_json`

```elixir
@spec to_json(t()) :: {:ok, String.t()} | {:error, [Macaw.Layers.Fault.t(), ...]}
```

The JSON document, `{:ok, json}`, stamped `"$version"`.

# `to_map`

```elixir
@spec to_map(t()) :: %{version: pos_integer(), surface: map()}
```

`%{version: current, surface: node}`.

# `validate`

```elixir
@spec validate(t(), Macaw.Registry.t(), %{
  required(String.t()) =&gt; {Macaw.Data.Source.t(), term()}
}) ::
  {:ok, Macaw.Surface.Validated.t()} | {:error, [Macaw.Layers.Fault.t(), ...]}
```

The static pass over a surface (spec/06 §3.4), with the host's registry and its sources
by name, each `{source, context}`: `{:ok, %Macaw.Surface.Validated{}}` or every fault at
once, by path from the surface. No I/O but each source's `describe`, and no viewer.

---

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