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

Puts a kit's part in the component it belongs to, when it arrives where none of those holds it.

A kit ships some components that are pieces of another one: Chelekom's Card Title, Card Media,
Card Content and Card Footer are sections of a Card, its Dropdown Trigger and Dropdown Content
the two halves of a Dropdown. Each renders on its own — it is a `<div>` — but the look is the
parent's, and a Card Footer dropped on a navbar landed in the navbar's `:end_content` slot as a
padded box nobody asked for. The drop that `MishkaCmsBuilder.TablePlacement` answers for a table
cell is the same gesture here: the part arrives inside a new parent, as one insert and one undo
step, and the author is told so. Inside its parent, at any depth, it lands as it is.

## What a part is

Read off the kit, never off the name. A component is a part of another when every example the kit
ships for it opens with that other component — the kit only ever shows it inside one — and the
two are one family in the kit's own catalogue (`extra.component`, `"card"` for all five Card
pieces). Both are needed: Chelekom shows its number field under a heading every time, and the
heading is not its home. A kit that names no families has no parts, and nothing changes for it.

The palette carries both facts, answered in its own query (`with_kit_facts/1`, `kit_facts/1`),
so a placement asks nothing of the database.

Only a part that is a plain box. One whose markup opens with an element HTML gives a home of its
own — a table row, a list item, a definition term, a word of phrasing — is answered by
`MishkaCmsCore.Runtime.Authoring.Containment` and `TablePlacement`, whose rules already say where
it may go; a second parent put around it here would land where HTML refuses it.

## Moves are not wrapped

A part already on the page that is dragged out of its parent is moved like any other node, because
nothing about it breaks the page. Wrapping is for a part arriving — from the palette, a snippet, a
paste. A component's own markup is a fragment placed elsewhere later, so a part at its top level
is left as it is too.

# `outcome`

```elixir
@type outcome() :: :none | {:held, String.t(), String.t()}
```

Whether a placement put a part's parent around it, and the two names the author is told.

# `holder`

```elixir
@spec holder(String.t(), [map()]) :: map() | nil
```

The palette entry of the component `name` belongs inside, or `nil` when it is not a kit's part —
see the moduledoc. Both have to be in `components`, the palette.

# `kit_facts`

```elixir
@spec kit_facts(map()) :: %{
  kit_family: String.t() | nil,
  example_heads: [String.t() | nil]
}
```

The facts `with_kit_facts/1` read for one row, as the palette entry carries them.

# `told`

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

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

# `with_kit_facts`

```elixir
@spec with_kit_facts(Ash.Query.t()) :: Ash.Query.t()
```

Answers in Postgres what `holder/2` asks of every row the palette reads: the component's family in
its kit's catalogue, and the component each of the kit's examples for it opens with.

Calculations, because `extra` is the column the palette must not pull whole — a component its kit
documents at length carries hundreds of kilobytes of examples, and these are a few short strings.

# `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 parent was put
around it.

Untouched when its first element is not a kit's part, or when `parent` or an element around it is
already that part's parent.

---

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