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).
Summary
Functions
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.
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.
Renders a handle: a point of a node where edges start (:source), end
(:target), or both.
Functions
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
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, aHakushi.Graph.on_change(:any) -fn change, context -> :ok | {:error, reason} end: the app's own check of each change, and where it saves it.contexthas thegraphbefore the change and theresultafter it, theuserand thecanvas. See the Changes guide. Defaults tonil.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 (itemisnil). A node with noidgets one. Defaults tonil.rules(:list) - the rules of the edges, seeHakushi.Rules. Defaults to[].live(:any) -{issho, topic}: a canvas that everyone in the Issho roomtopicshares, seeHakushi.Issho. Defaults tonil.user(:map) - the person, on a shared canvas: a map with an:id, and a:name,:colorand:avatar. Defaults tonil.background(:atom) - the paper: dots, lines (graph paper), crosses, or none. Defaults to:dots. Must be one of:dots,:lines,:cross, or:none.grid(:integer) - the size of a square of the paper, in pixels at 100%. Defaults to20.snap(:boolean) - whentrue, nodes snap to the paper's grid. Defaults tofalse.wheel(:atom) - what the mouse wheel does: pan (and zoom with ⌘ or Ctrl), or zoom. Defaults to:pan. Must be one of:pan, or:zoom.fit(:boolean) - whentrue, the canvas opens on all of its nodes. Defaults totrue.min_zoom(:float) - the smallest zoom. Defaults to0.1.max_zoom(:float) - the largest zoom. Defaults to4.0.minimap(:boolean) - whentrue, an overview of the canvas in a corner. Defaults totrue.controls(:boolean) - whentrue, buttons to zoom, fit, tidy up, undo and redo, and to download the canvas. Defaults totrue.layout(:atom) - the way the tidy up (⇧L, the controls) lays the nodes out: in layers from left to right, or from top to bottom; see Layout and export. Defaults to:right. Must be one of:right, or:down.export(:boolean) - whentrue, a button of the controls downloads the canvas as an image. Defaults totrue.readonly(:boolean) - whentrue, people pan, zoom and select, and change nothing. Defaults tofalse.label(:string) - the name of the canvas, for screen readers. Defaults tonil.node_label(:any) -fn node -> text end: the name of a node for screen readers; the default is its data'slabelorname, or its id. Defaults to&Hakushi.Canvas.label/1.locale(:string) - the locale of the canvas's own words; the default isconfig :hakushi, locale:, else "en". Defaults tonil.messages(:map) - words that replace the built-in ones, seeHakushi.Messages. Defaults tonil.class(:any) - classes for the canvas element. Defaults tonil.- Global attributes are accepted. attributes for the canvas element.
Slots
node- draws the nodes of a type (:letgets theHakushi.Node). Accepts attributes:type(:string) - the type of the nodes it draws; none for every other type.resizable(:boolean) - whentrue, people resize these nodes by their corners.group(:boolean) - whentrue, these nodes hold others: a node dropped on one goes in it, see Groups.
person- draws a person on a shared canvas (:letgets a map with "id", "name", "color", "avatar").empty- shows on a canvas without nodes.
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, fornew_node.class(:any) - Defaults tonil.- Global attributes are accepted.
Slots
inner_block(required)
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_handleortarget_handle. Defaults tonil.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, foraccepts. Defaults tonil.accepts(:list) - the types it takes;nilfor any. Defaults tonil.max(:integer) - the most edges it has;nilfor any number. Defaults tonil.label(:string) - its name, for screen readers. Defaults tonil.class(:any) - Defaults tonil.- Global attributes are accepted.