An edge is a Hakushi.Edge: from a handle of one node (source,
source_handle) to a handle of another (target, target_handle).
%{source: "orders", source_handle: "user_id", target: "users", target_handle: "id",
path: "smooth", label: "belongs to", marker: "arrow"}How it is drawn
path | "bezier" (the default), "smooth" (steps with round corners), "step" (square corners), "straight" |
marker | the end at the target: "arrow" (the default), "dot", "none" |
label | a text on its middle; people set it with a double click on the edge |
animated | a dashed line that flows from the source to the target (still with reduced motion) |
An edge leaves and arrives square to the sides of its handles.
Drawing an edge
A person drags from a handle. While they drag, the handles the edge may end on show, and the others fade; the edge snaps to the nearest one within reach, or to the nearest valid handle of the node under the pointer. Dragged from a target, the edge is drawn backwards: it starts at the handle it ends on.
From the keyboard: focus a node, press C, choose its other end with the arrows (the valid ends in turn, the nearest first), and press Enter. See Navigation.
The new edge goes to the server, which checks it again (below) and asks
on_change; until it answers, the edge shows as a dashed line.
What connects to what
The rules of the canvas (Hakushi.Rules), the same in the browser and on
the server:
<.canvas id="flow" graph={@graph} rules={[cycles: false]}>loops: an edge from a node to itself (falseunless set).duplicates: a second edge between the same handles, in the same direction (falseunless set).cycles: an edge that closes a cycle (trueunless set). A workflow sets it tofalse.
The handles add their own, in the browser:
- a handle with
acceptstakes edges from handles whosetypeis in it: a column ofuuidfrom a column ofuuid; - a handle with
maxtakes no more edges than that: a condition's "Yes" goes one way.
The server does not know the handles (they are in the HEEx of each
node). Check what an edge means for your app in on_change, where you
have your data:
def change(%Hakushi.Change{op: :connect, edges: [edge]}, %{graph: graph}) do
if column_type(graph, edge.source, edge.source_handle) == column_type(graph, edge.target, edge.target_handle),
do: :ok,
else: {:error, "These columns are not of one type."}
endMoving an end
A selected edge shows its two ends: drag one to another handle (or
node), and the edge goes there, by the same rules as a new edge, which
the handles it may end on show. The edge keeps its id, its label and its
data. On the server it is a :reconnect change, which the rules check
with the edge's new ends and on_change sees; ⌘Z puts it back.
Selecting and deleting
A click on an edge or its label selects it; Shift adds it. Delete deletes the selected edges and nodes; the edges of a deleted node go with it.