# `MishkaCmsBuilder.TablePlacement`
[🔗](https://github.com/mishka-group/mishka_cms_builder/blob/v0.0.1-alpha.2/lib/mishka_cms_builder/table_placement.ex#L5)

Puts a table around a cell, a row or a table section dropped where no table holds it.

A parser ignores a `<td>` written outside a table, so the core refuses one there
(`MishkaCmsCore.Runtime.Authoring.Containment`). Refused and nothing else, a Table cell card
dropped on a page would do nothing at all, and an author who wanted a table would have to know to
start from the Table card. The drop that FormPlacement answers for a field — wrap it in what it
needs, and say so — is the same gesture here: the part arrives inside the smallest table structure
its landing place takes, as one insert and one undo step.

## What it is wrapped in

A kit's own part gets the kit's own row and table, found by their markup rather than by name: a
component whose markup opens with a `<tr>` and renders its content in it is a row, and one that
renders its content in a `<tbody>` or a `<table>` is a table. The kit's cell is styled by the kit's
table, so a bare `<table>` around it would look like a different component. A raw part, or a kit
with no row or table of its own, gets plain `<tr>`, `<tbody>` and `<table>` tags.

Only as much as the landing place needs: a cell dropped into a table section gets a row, a cell
dropped on a page gets a row and a table.

## Moves are not wrapped

A cell dragged out of its row is refused and climbs back to where a cell may go, as any refused
move does. Wrapping is for a part arriving from the palette.

# `outcome`

```elixir
@type outcome() :: :none | {:tabled, pos_integer()}
```

Whether a placement put a table around what arrived, and how many levels deep the part sits in it
(`{:tabled, 2}` for a cell in a new row and table).

# `arrivals`

```elixir
@spec arrivals(String.t(), map()) :: [
  {MishkaCmsCore.Runtime.Authoring.Tree.Node.t(),
   MishkaCmsCore.Runtime.Authoring.Tree.t()}
]
```

The heads a table part could arrive as — the part itself and every wrapper it may be given — each
with its own tree. A drag asks every one of them where it may land (`drop_hints`), because the
drop puts the wrapper around it there. `[]` for anything that is not a table part.

# `fragment?`

```elixir
@spec fragment?(map()) :: boolean()
```

Whether a component's own markup is a fragment placed elsewhere later, whose top level may hold a
table part: the `fragment?` option `Containment.can_drop?/4` takes.

# `part`

```elixir
@spec part(MishkaCmsCore.Runtime.Authoring.Tree.t(), String.t() | nil, outcome()) ::
  MishkaCmsCore.Runtime.Authoring.Tree.Node.t() | nil
```

The part a placement put a table around, found under the head that was inserted: `depth` levels
down, each wrapper holding only what it was put around. The head itself for `:none`, and `nil` when
the tree no longer has that shape.

# `told`

```elixir
@spec told(outcome()) :: String.t() | nil
```

What the author is told after a placement, or `nil` when there is nothing to say.

# `wrap`

```elixir
@spec wrap(String.t(), map(), String.t() | :root | nil) :: {String.t(), outcome()}
```

The template to insert at `parent` (a node id, `"root"` or `:root`), and whether a table was put
around it — with how many levels it put, so the part can be found again under the new head
(`part/3`).

Untouched when its first element is not a table part, or when `parent` already holds it.
Otherwise it is wrapped in the first of its possible wrappers that `parent` takes, or in the whole
table when none is — which a refused drop then carries as it climbs.

---

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