# `Hakushi.Components`
[🔗](https://github.com/hattoriai/hakushi/blob/v0.6.0/lib/hakushi/components.ex#L1)

Function components for the canvas.

    import Hakushi.Components

    <.canvas id="schema" graph={@graph} on_change={&Schema.change/2}>
      <:node type="table" :let={node}>
        <div class="table">
          <h3>{node.data.name}</h3>
          <div :for={column <- node.data.columns} class="relative">
            <.handle id={column.name} kind={:target} side={:left} />
            {column.name}
            <.handle id={column.name} kind={:source} side={:right} />
          </div>
        </div>
      </:node>
    </.canvas>

`canvas/1` needs the `Hakushi` hook in the LiveSocket and the Hakushi
CSS (see the README).

# `canvas`

Renders a canvas: an endless sheet that people pan and zoom, with the
nodes of `graph` on it, drawn by the `:node` slots, and its edges.

People move nodes by dragging them (or with the arrow keys), select
them with a click, Shift and a box, connect them by dragging from a
handle to a handle (or with C, see the [Navigation](navigation.md)
guide), delete them with Delete, and copy, paste, duplicate, undo and
redo with the usual keys. Each change goes to the server, which checks
it (`rules`, then `on_change`) before anyone sees it, and takes it back
in the browser when it refuses it.

## Nodes

A `:node` slot draws the nodes of its `type`; a `:node` slot without
a `type` draws the others. The slot gets the `Hakushi.Node`, and draws
it from its `data`, with the handles where edges start and end
(`handle/1`). A canvas with no `:node` slot draws each node as a box
with its label.

A node is drawn once, and again when it changes: a change of its
`data` (a change `:update`, or a new `graph`) draws it again; a change
of the LiveView's own assigns does not.

## The graph

Without `live`, the canvas keeps its own copy of `graph`, which its
changes change. Give it a new `graph` (after the app's own change, say)
and it shows what changed. With `live`, the graph is the room's (see
`Hakushi.Issho`): make the app's changes with `Hakushi.Live.change/2`
or `Hakushi.Issho.change/3`.

## Attributes

* `id` (`:string`) (required) - the id of the canvas.
* `graph` (`Hakushi.Graph`) (required) - the nodes and edges, a `Hakushi.Graph`.
* `on_change` (`:any`) - `fn change, context -> :ok | {:error, reason} end`: the app's own check of each change, and where it saves it. `context` has the `graph` before the change and the `result` after it, `by`, who made it (on a shared canvas an `Issho.Identity`: a person, an agent and whom it acts for, or the server; alone, the canvas's `user`), and the `canvas`. See the [Changes](changes.md) guide. Defaults to `nil`.
* `new_node` (`:any`) - `fn item, {x, y} -> node | nil end`: the node for an item dropped on the canvas (`canvas_item/1`), or for a double click on the sheet (`item` is `nil`). A node with no `id` gets one. Defaults to `nil`.
* `rules` (`:any`) - the rules of the edges, see `Hakushi.Rules`; they win over the rules of the kits. Defaults to `[]`.
* `kits` (`:list`) - kits (`Hakushi.Kit`) whose nodes the canvas draws, makes and edits by itself: `[Hakushi.Kits.Schema]`; see the [Kits](kits.md) guide. Defaults to `[]`.
* `issho` (`:any`) - a canvas that everyone in its Issho room shares: `[instance: MyApp.Issho, document: id]`, with the keys of every component (`Issho.Join`: `:can`, `:people`, `:follow`, `:on_behalf_of`, `:claims`); see `Hakushi.Issho`. Defaults to `nil`.
* `live` (`:any`) - removed in 0.3: `issho`; see the Upgrading guide. Defaults to `nil`.
* `user` (`:map`) - the person, on a shared canvas: the app's user, as its `Issho.Actor` says them (or a map with an `:id`, and a `:name`, `:color` and `:avatar`). Defaults to `nil`.
* `background` (`:any`) - the paper: `:dots`, `:lines` (graph paper), `:cross`, `:none`, or the name of a paper of the app's own (see `Hakushi.Paper`); the default is the first kit's (`c:Hakushi.Kit.canvas/0`), else dots. A paper people chose (`papers`) wins. Defaults to `nil`.
* `grid` (`:integer`) - the size of a square of the paper, in pixels at 100%; the default is a kit's, else 20. Defaults to `nil`.
* `snap` (`:boolean`) - when `true`, nodes snap to the paper's grid; the default is a kit's, else `false`. Defaults to `nil`.
* `papers` (`:any`) - the papers people choose from, in a picker of the controls: `true` for Hakushi's (dots, graph paper, crosses, none), or a list of them (`:dots`) and of the app's own (`%{name: "iso", label: "Isometric"}`); none by default. The paper chosen is the graph's (a change, which undoes and which everyone sees); see `Hakushi.Paper`. Defaults to `nil`.
* `wheel` (`:atom`) - what the mouse wheel does: pan (and zoom with ⌘ or Ctrl), zoom, or scroll the page (and zoom with ⌘, Ctrl or a pinch), for a canvas in a page that scrolls, as an embed. Defaults to `:pan`. Must be one of `:pan`, `:zoom`, or `:scroll`.
* `fit` (`:any`) - when `true`, the canvas opens on all of its nodes; when `:readable`, on all of them if they are large enough to read (60% or more), else on its most joined node and its neighbours, at a zoom people read: for a large graph (a schema of hundreds of tables), whose whole would show no detail. Defaults to `true`. Must be one of `true`, `false`, or `:readable`.
* `refit` (`:boolean`) - when `true`, the canvas shows all of its nodes again when its box changes size, as an embed that goes full screen and back. Defaults to `false`.
* `min_zoom` (`:float`) - the smallest zoom. Defaults to `0.1`.
* `max_zoom` (`:float`) - the largest zoom. Defaults to `4.0`.
* `minimap` (`:boolean`) - when `true`, an overview of the canvas in a corner. Defaults to `true`.
* `controls` (`:boolean`) - when `true`, buttons to zoom, fit, tidy up, undo and redo, and to download the canvas. Defaults to `true`.
* `layout` (`:any`) - the way the tidy up (⇧L, the controls) lays the nodes out: in layers from left to right (`:right`) or from top to bottom (`:down`), by forces (`:force`), or in a grid (`:grid`); `:layered` is `:right`, and `{:layered, :down}` is `:down`. The default is a kit's, else `:right`. People choose another in the menu by the tidy up button; see [Layout and export](layout.md). Defaults to `nil`.
* `tidy` (`:boolean`) - when `true`, the canvas lays its nodes out (its `layout`) as it opens, before people see them, for a graph whose nodes have no places of their own (a schema read from a database); not on a shared canvas, whose places are the room's. See [Layout and export](layout.md). Defaults to `false`.
* `export` (`:boolean`) - when `true`, a button of the controls downloads the canvas as an image. Defaults to `true`.
* `readonly` (`:any`) - when `true`, people pan, zoom and select, and change nothing; when `:arrange`, they also move the nodes and tidy them up (and undo that), and change nothing else, as a diagram made from data; see [Layout and export](layout.md). Defaults to `false`. Must be one of `true`, `false`, or `:arrange`.
* `follow` (`:any`) - removed in 0.3: `issho`'s `:follow`. Defaults to `nil`.
* `label` (`:string`) - the name of the canvas, for screen readers. Defaults to `nil`.
* `node_label` (`:any`) - `fn node -> text end`: the name of a node for screen readers; the default is its data's `label` or `name`, or its id. Defaults to `&Hakushi.Canvas.label/1`.
* `locale` (`:string`) - the locale of the canvas's own words; the default is `config :hakushi, locale:`, else "en". Defaults to `nil`.
* `messages` (`:map`) - words that replace the built-in ones, see `Hakushi.Messages`. Defaults to `nil`.
* `class` (`:any`) - classes for the canvas element. Defaults to `nil`.
* Global attributes are accepted. attributes for the canvas element.
## Slots

* `node` - draws the nodes of a type (`:let` gets the `Hakushi.Node`). Accepts attributes:

  * `type` (`:string`) - the type of the nodes it draws; none for every other type.
  * `resizable` (`:boolean`) - when `true`, people resize these nodes by their corners.
  * `group` (`:boolean`) - when `true`, these nodes hold others: a node dropped on one goes in it, see [Groups](groups.md).
* `person` - draws a person on a shared canvas (`:let` gets a map with "id", "name", "color", "avatar").
* `empty` - shows on a canvas without nodes.

# `canvas_embed`

Renders a canvas in a frame of its own, for a page that has other
things than the canvas: a document (a Kotoba embed), a card, a panel.
The frame has a bar with the canvas's icon and title, the items of its
kits (`kit_items/1`), the app's own actions, and a button that takes it
full screen and back.

    <.canvas_embed title={@diagram.title} for="diagram" kits={@kits} full={@full} on_full={JS.push("full")}>
      <.canvas id="diagram" graph={@diagram.graph} kits={@kits} wheel={:scroll} refit />
    </.canvas_embed>

The frame takes the height of its parent (`--hakushi-embed-height`
when the parent has none). Give its canvas `wheel={:scroll}`, so that
the wheel scrolls the page around it, and `refit`, so that it shows all
of its nodes when it goes full screen and back.

The frame does not go full screen by itself: `on_full` is the app's
(a `Phoenix.LiveView.JS` command or an event name), and `full` says
which way the button goes. A Kotoba embed gives both (see the
[Kits](kits.md) guide).

## Attributes

* `title` (`:string`) (required) - the canvas's title.
* `icon` (`:string`) - a name of `Hakushi.Icons`. Defaults to `"shapes"`.
* `subtitle` (`:string`) - words after the title, as the kind of canvas. Defaults to `nil`.
* `for` (`:string`) - the id of the canvas, for the items of `kits`. Defaults to `nil`.
* `kits` (`:list`) - the kits whose items the bar has. Defaults to `[]`.
* `readonly` (`:boolean`) - when `true`, the bar has no items. Defaults to `false`.
* `full` (`:boolean`) - whether the frame is full screen now. Defaults to `false`.
* `on_full` (`:any`) - the `phx-click` of the full screen button; none without it. Defaults to `nil`.
* `locale` (`:string`) - the locale of the words, as the canvas's. Defaults to `nil`.
* `messages` (`:map`) - words that replace the built-in ones. Defaults to `nil`.
* `class` (`:any`) - Defaults to `nil`.
* Global attributes are accepted.
## Slots

* `heading` - the app's own heading in place of the title, as a field to rename the canvas.
* `actions` - the app's own buttons, before the full screen button.
* `inner_block` (required) - the canvas.

# `canvas_item`

Renders an item that people drag onto a canvas, outside it: a palette
of the things the canvas makes. Dropped on the canvas, it becomes the
node that the canvas's `new_node` gives for `item`. Enter or a click
puts it in the middle of the canvas, for the keyboard and for a touch
screen.

    <.canvas_item for="schema" item="table">New table</.canvas_item>

## Attributes

* `for` (`:string`) (required) - the id of the canvas.
* `item` (`:string`) (required) - what it makes, for `new_node`.
* `class` (`:any`) - Defaults to `nil`.
* Global attributes are accepted.
## Slots

* `inner_block` (required)

# `handle`

Renders a handle: a point of a node where edges start (`:source`), end
(`:target`), or both.

A handle sits on a `side` of its nearest positioned ancestor: the node,
or an element of it with `position: relative`, as a row of a table
whose edges start at the row.

    <div :for={column <- node.data.columns} class="relative">
      {column.name}
      <.handle id={column.name} side={:right} type={column.type} />
    </div>

A node may have several handles with one `id`, on different sides (a
row with a handle at each end): an edge meets the one nearest its
other end, so the line takes the short way whichever side the other
node is on.

`type` and `accepts` say what connects to what: a handle connects to a
handle that `accepts` its `type` (any, when `accepts` is `nil`). While
a person draws an edge, the handles it may end on show, and no others.

## Attributes

* `id` (`:string`) - the id of the handle in its node, the edges' `source_handle` or `target_handle`. Defaults to `nil`.
* `kind` (`:atom`) - where edges start (`:source`), end (`:target`), or both. Defaults to `:both`. Must be one of `:source`, `:target`, or `:both`.
* `side` (`:atom`) - the side it sits on. Defaults to `:right`. Must be one of `:left`, `:right`, `:top`, or `:bottom`.
* `type` (`:string`) - what its edges carry, for `accepts`. Defaults to `nil`.
* `accepts` (`:list`) - the types it takes; `nil` for any. Defaults to `nil`.
* `max` (`:integer`) - the most edges it has; `nil` for any number. Defaults to `nil`.
* `label` (`:string`) - its name, for screen readers. Defaults to `nil`.
* `class` (`:any`) - Defaults to `nil`.
* Global attributes are accepted.

# `kit_items`

Renders the items of kits, outside a canvas: one for each element of
each kit (a table, a step, a note), which people drag onto the canvas,
or click (or Enter) to put one in its middle. See `canvas_item/1`.

    <.kit_items for="model" kits={[Hakushi.Kits.Schema]} />

## Attributes

* `for` (`:string`) (required) - the id of the canvas.
* `kits` (`:list`) (required) - the canvas's kits.
* `locale` (`:string`) - the locale of the words, as the canvas's. Defaults to `nil`.
* `messages` (`:map`) - words that replace the built-in ones. Defaults to `nil`.
* `class` (`:any`) - Defaults to `nil`.
* Global attributes are accepted.

---

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