# Trees

`tree/1` is a tree that people reorder and nest: pages, folders and
files, an outline. `tree_picker/1` chooses a place in a tree, in a form.

```heex
<.tree id="pages" items={@pages} item_label={:title} item_kind={:kind} rules={Page} on_move="move" filter label="Pages" />
```

## The items

`items` is flat, as the database has it: each item with its parent's id
(`item_parent`, `:parent_id` by default), in their order. Read them with
`Narabe.Ecto.ordered/2` and the tree shows each under its parent
(`Narabe.Tree.rows/2`). An item whose parent is not among the items is at
the top, so a tree may show one branch, or the levels loaded so far.

The schema says that its group is its parent:

```elixir
defmodule MyApp.Docs.Page do
  use Ecto.Schema

  schema "pages" do
    field :title, :string
    field :kind, :string
    field :position, :string
    belongs_to :parent, __MODULE__
  end

  use Narabe.Ordered,
    group: :parent_id,
    tree: true,
    kind: :kind,
    rules: [nest: true, max_depth: 6, leaves: ["file"]]
end
```

`rules={Page}` gives the tree the same rules as `Narabe.Ecto` (see the
[Rules](rules.md) guide). A tree's rules nest when none are given.

## The move

A move's `"parent"` is the new parent's id, or nil at the top level. The
item's subtree moves with it. The LiveView applies it as a list's:

```elixir
def handle_event("move", params, socket) do
  result = Narabe.Ecto.move(Repo, Page, Narabe.Move.parse!(params, Page))
  {:reply, Narabe.Live.reply(result), load_pages(socket)}
end
```

`Narabe.Ecto` refuses a move into the item itself or under it
(`:into_itself`), and checks the rules. In memory,
`Narabe.Move.apply(pages, move, group: :parent_id, tree: true)` does the
same.

## Where a drop lands

With a pointer, the top quarter of a row is the gap before it, the bottom
quarter the gap after it, and the middle is inside it, when it may hold
children. A drop inside a closed row puts the items at the end of its
children. In a gap, moving the pointer left and right picks the depth,
one indent (`--narabe-indent`) per level, between what the rows above
and below allow.

With the keyboard, Space lifts, Up and Down pick the gap, Right goes one
level deeper and Left one level up, Space drops.

## Opening and closing

Rows open and close with a click on their arrow, or Right and Left.
`expanded` gives the ids open at first. From the server,
`Narabe.Live.expand/4` opens or closes rows, and `Narabe.Live.reveal/3`
opens a row's ancestors, scrolls to it and focuses it.

## Children loaded as they open

`item_children` says which items have children that are not loaded yet
(a key or a function). Such an item shows an arrow; as it opens, the
tree sends `on_expand` with `%{"v" => 1, "id" => id}` and shows a
spinner until its children are in `items`:

```heex
<.tree id="pages" items={@pages} item_children={:has_children} on_expand="expand" on_move="move" />
```

```elixir
def handle_event("expand", %{"id" => id}, socket) do
  children = Page |> where(parent_id: ^id) |> Narabe.Ecto.ordered() |> Repo.all()
  {:noreply, update(socket, :pages, &(&1 ++ children))}
end
```

Order matters only among siblings: the children may come after the rest.

## Filter

With `filter`, a box above the tree shows the items whose label has the
text (case and accents do not count), with their ancestors dimmed for
context. The matched letters are marked. Nothing moves while the box has
text. Only the loaded items are searched.

## Rename

With `on_rename`, F2 or a double click on a label opens a box in place.
Enter or leaving the box saves, Escape cancels. The tree shows the new
name at once and sends `%{"v" => 1, "id" => id, "name" => name}`:

```elixir
def handle_event("rename", %{"id" => id, "name" => name}, socket) do
  page = Repo.get!(Page, id)
  {:ok, _} = page |> Ecto.Changeset.change(title: name) |> Repo.update()
  {:noreply, load_pages(socket)}
end
```

With an `item` slot of your own, mark the element of the label with
`data-narabe-label`, so the rename box and the filter's marks find it:

```heex
<:item :let={page}>
  <.icon name={icon(page)} />
  <span data-narabe-label>{page.title}</span>
</:item>
```

## On a phone

A tree shows as a tree on a phone. With `phone={:drill}`, on a touch
screen 640 px wide or less, it shows one level at a time: a tap on a row
with children opens its level, and a bar at the top goes back. A held
finger reorders the items within the level shown.

## The tree picker

`tree_picker/1` is a field of a form that chooses an item of a tree: a
folder, a parent page, a category. It posts the item's id, as a
`<select>` would:

```heex
<.form for={@form} id="move-form" phx-change="validate" phx-submit="save">
  <.tree_picker field={@form[:parent_id]} items={@folders} item_label={:title} disabled={[@page.id]} label="Folder" />
</.form>
```

A click, Space or Enter picks an item. `disabled` ids, and the items
under them, show and cannot be picked (the page being moved, so it does
not go into itself). The chosen item's ancestors are open at first,
unless `expanded` says otherwise. The picker filters by default
(`filter={false}` turns it off) and drills on a phone by default
(`phone={:tree}` shows the tree). Nothing drags in a picker. It
dispatches `narabe:pick` in the page, with the id in its `detail`.

## The tree in the database

`Narabe.Ecto.ancestors/4` gives an item's ancestors' ids, parent first;
`Narabe.Ecto.descendants/4` its descendants' ids. Each is one recursive
query. See the [Database](database.md) guide.
