Current section
Files
Jump to
Current section
Files
guides/quickstart.md
# Quickstart
A canvas in a LiveView, its graph from your database, and each change
saved back.
## Install
Follow the README's [Install](../README.md#install): the dependency, the
hook in the `LiveSocket`, and the style sheet. Then import the components
in the `html_helpers` of your web module:
```elixir
import Hakushi.Components
```
## A canvas
```elixir
defmodule MyAppWeb.FlowLive do
use MyAppWeb, :live_view
alias Hakushi.Graph
def mount(%{"id" => id}, _session, socket) do
flow = Flows.get!(id)
{:ok, assign(socket, flow: flow, graph: flow.graph)}
end
def render(assigns) do
~H"""
<.canvas id="flow" graph={@graph} on_change={&save(&1, &2, @flow)} class="h-[calc(100vh-4rem)]">
<:node type="step" :let={node}>
<div class="rounded-lg border bg-white px-4 py-3 shadow-sm">
<.handle kind={:target} side={:left} />
{node.data["title"]}
<.handle kind={:source} side={:right} />
</div>
</:node>
</.canvas>
"""
end
defp save(_change, %{result: graph}, flow) do
Flows.update_graph(flow, graph)
end
end
```
The canvas fills its box: give it a height. It opens on all of its
nodes (`fit`), on dotted paper (`background`).
## The graph in one column
`Hakushi.Graph.Type` keeps a whole canvas in one column of a table, as a
map (`jsonb` on PostgreSQL):
```elixir
schema "flows" do
field :name, :string
field :graph, Hakushi.Graph.Type, default: Hakushi.Graph.new()
end
```
```elixir
# the migration
add :graph, :map, null: false, default: %{}
```
```elixir
def update_graph(flow, graph) do
flow |> Ecto.Changeset.change(graph: graph) |> Repo.update()
end
```
`on_change` returns `:ok`, `{:ok, anything}` (as `Repo.update/1` does),
`{:error, reason}` with the words to show, or `{:error, changeset}`,
whose first error is shown. A change that `on_change` refuses does not
happen: the browser takes it back. See [Changes](changes.md).
A canvas whose nodes are rows of their own (the tables of a schema, the
steps of a workflow with their settings) keeps them in its own tables,
makes its graph of them in `mount`, and changes the rows in `on_change`
by the kind of change.
## New nodes
People make nodes by dragging an item onto the canvas, by pressing
Enter on it, or by a double click on the paper. The canvas asks
`new_node` for the node:
```heex
<.canvas_item for="flow" item="email">Email</.canvas_item>
<.canvas id="flow" graph={@graph} on_change={...} new_node={&new_step/2}>
```
```elixir
defp new_step(kind, {x, y}),
do: %{type: "step", x: x, y: y, data: %{"kind" => kind || "note", "title" => "New step"}}
```
A node without an id gets one. `new_node` returning `nil` makes none.
## Next
* [Nodes](nodes.md) and [Edges](edges.md): what the canvas draws.
* [Collaboration](collaboration.md): one canvas for everyone on it.
* [Testing](testing.md): the canvas in `Phoenix.LiveViewTest`.