Graph Apps With LivePanels

Copy Markdown View Source

Use this track when the canvas is primarily a graph editor, workflow builder, schema diagram, dependency map, or node-and-connector application.

The shorter Add Graph Connections guide shows how graph support fits into a canvas. This track goes deeper: node modeling, port declarations, connector layers, connection gestures, reconnect UX, routing, labels, hit targets, layout, navigation, persistence, shared canvases, and recipes.

What LivePanels Gives A Graph App

LivePanels gives graph apps a server-authoritative canvas model:

  • graph nodes are canvas items;
  • ports are declared item capabilities;
  • connectors live in canvas state;
  • connection gestures become commands;
  • connector projection derives SVG paths from item placement and port geometry;
  • selection, history, clipboard, persistence, and shared mode keep working because graph state stays inside the canvas model.

Your application still owns the product:

  • what a node means;
  • what a port means;
  • how nodes and ports look;
  • how connectors are styled;
  • whether a connection creates a domain record;
  • how users browse, filter, inspect, and edit the graph.

The Graph App Loop

A graph app usually follows this loop:

author node items
  -> declare ports on those item attrs
  -> render application node markup
  -> render application port controls with graph attrs
  -> let canvas_runtime render connector items in the world SVG projection
  -> style runtime connector classes and data attrs
  -> send graph commands from browser attrs or server code

The important part is that the graph does not become a separate client-side store. The graph is a capability on the canvas.

Minimum Graph Canvas

Start with the graph toolkit:

defmodule MyAppWeb.FlowLive do
  use MyAppWeb, :live_view

  use LivePanels.Canvas,
    pubsub: MyApp.PubSub

  use LivePanels.Toolkit.Graph

  def render(assigns) do
    ~H"""
    <.canvas_runtime {canvas_runtime_attrs(assigns)}>
      <:item
        id="step-1"
        type="step"
        at={{120, 80}}
        size={{520, 360}}
        ports={[
          %{name: "in", direction: :input, type: :task},
          %{name: "out", direction: :output, type: :task}
        ]}
      >
        <.step_card />
      </:item>
    </.canvas_runtime>
    """
  end
end

Render the canvas and node ports. Connector items render through the runtime world SVG item projection:

<.canvas_runtime {canvas_runtime_attrs(assigns)}>
  <:item :let={context}>
    <article class="node">
      <header {drag_handle_attrs()}>{context.item.label}</header>

      <button
        :for={port <- context.ports}
        {graph_port_anchor_attrs(port)}
        {if port.direction == :output,
          do: begin_connector_attrs(context.item, port),
          else: complete_connector_attrs(context.item, port)}
      >
        {port.label}
      </button>

      {context.content}
    </article>
  </:item>
</.canvas_runtime>

That is the foundation. The rest of this track shows how to turn it into a real graph application.

Track Map

Read the guides in this order when building a graph app:

  • Model Nodes, Ports, And Connectors for the runtime data model.
  • Render Nodes And Ports for item shells, port placement, and application UI.
  • Render Connector Items for runtime SVG connector rendering and styling.
  • Connect, Reconnect, And Disconnect for graph commands and gestures.
  • Select, Edit, Route, Label, And Hit-Test Connectors for graph editor behavior.
  • Layout, Fit, And Navigate Graphs for graph layout and viewport movement.
  • Persist And Share Graph Canvases for recovery and collaboration.
  • Graph App Recipes for common application patterns.

What To Keep Out Of The Core Runtime

Do not put product meaning into LivePanels.

A connection might mean "foreign key", "workflow transition", "message route", "dependency", "lineage", or "related idea". LivePanels only needs to know that a connector connects endpoint A to endpoint B and that the command is valid.

Keep domain records in your app. Use LivePanels to make the canvas behavior consistent.