Event Handlers
Declare these methods on your Livewire component to handle canvas events. Wire them up via event attributes on <x-flow>:
<x-flow :nodes="$nodes" :edges="$edges"
@connect="onConnect"
@node-click="onNodeClick"
@node-drag-end="onNodeDragEnd"
@pane-click="onPaneClick"
>
<x-slot:node>
<x-flow-handle type="target" position="top" />
<span x-text="node.data.label"></span>
<x-flow-handle type="source" position="bottom" />
</x-slot:node>
</x-flow>
Only the events you reference with @event-name are dispatched to the server. Omit an attribute to skip that event entirely.
Connection events
public function onConnect(
string $source, // Source node ID
string $target, // Target node ID
?string $sourceHandle, // Source handle ID (null if default)
?string $targetHandle, // Target handle ID (null if default)
): void {
// A new edge was created by the user.
// Typically you'd store the edge:
$this->edges[] = [
'id' => "e-{$source}-{$target}",
'source' => $source,
'target' => $target,
'sourceHandle' => $sourceHandle,
'targetHandle' => $targetHandle,
];
}
public function onConnectStart(
string $source, // Source node ID
?string $sourceHandle, // Source handle ID
): void {
// User started dragging from a handle
}
public function onConnectEnd(
?array $connection, // ['source', 'target', 'sourceHandle', 'targetHandle'] or null if cancelled
?string $source, // Source node ID
?string $sourceHandle, // Source handle ID
?array $position, // ['x' => float, 'y' => float] -- flow coordinates where drag ended
): void {
// Drag finished -- $connection is null if user released on empty canvas
}
Node events
public function onNodeClick(
string $nodeId, // Clicked node ID
array $node, // Full node array: ['id', 'position' => ['x','y'], 'data' => [...], ...]
): void {
// Handle node click
}
public function onNodeDragStart(string $nodeId): void {
// User started dragging a node
}
public function onNodeDragEnd(
string $nodeId,
array $position, // ['x' => float, 'y' => float] -- final flow-space position
): void {
// Update the server-side position if using sync mode
}
public function onNodeResizeStart(
string $nodeId,
array $dimensions, // ['width' => float, 'height' => float]
): void {
// User started resizing a node
}
public function onNodeResizeEnd(
string $nodeId,
array $dimensions, // ['width' => float, 'height' => float] -- final dimensions
): void {
// Persist the new dimensions if needed
}
public function onNodeCollapse(string $nodeId): void {
// A group node was collapsed
}
public function onNodeExpand(string $nodeId): void {
// A group node was expanded
}
public function onNodeReparent(
string $nodeId,
?string $newParentId, // New parent ID (null if detached)
?string $oldParentId, // Previous parent ID (null if was root)
): void {
// A node was drag-reparented into or out of a group
}
public function onNodeContextMenu(
string $nodeId,
array $screenPosition, // ['x' => float, 'y' => float] -- screen coordinates
): void {
// Right-click on a node -- use screenPosition to place a context menu
}
public function onNodesChange(array $changes): void {
// Batch notification of node additions or removals
// $changes = ['type' => 'add'|'remove', 'nodes' => [['id' => '...', ...], ...]]
}
Edge events
public function onEdgeClick(string $edgeId): void {
// User clicked an edge
}
public function onEdgeContextMenu(
string $edgeId,
array $screenPosition, // ['x' => float, 'y' => float]
): void {
// Right-click on an edge
}
public function onEdgesChange(array $changes): void {
// Batch notification of edge additions or removals
// $changes = ['type' => 'add'|'remove', 'edges' => [['id' => '...', ...], ...]]
}
public function onReconnect(
string $oldEdgeId,
array $newConnection, // ['source', 'target', 'sourceHandle', 'targetHandle']
): void {
// An edge endpoint was dragged to a new node
}
public function onReconnectStart(
string $edgeId,
string $handleType, // 'source' or 'target' -- which end is being reconnected
): void {
// User started reconnecting an edge
}
public function onReconnectEnd(
string $edgeId,
bool $successful, // true if reconnected, false if cancelled
): void {
// Reconnect attempt finished
}
Canvas events
public function onPaneClick(array $position): void {
// Clicked empty canvas area
// $position = ['x' => 100, 'y' => 200] (flow coordinates)
}
public function onPaneContextMenu(array $position): void {
// Right-click on empty canvas
}
public function onViewportChange(array $viewport): void {
// Viewport panned or zoomed
// $viewport = ['x' => 0, 'y' => 0, 'zoom' => 1.0]
}
Selection events
public function onSelectionChange(array $nodes, array $edges): void {
// Selection changed
// $nodes = ['node-1', 'node-2'] -- selected node IDs
// $edges = ['edge-1'] -- selected edge IDs
}
public function onSelectionContextMenu(
array $nodes,
array $edges,
array $screenPosition, // ['x' => float, 'y' => float]
): void {
// Right-click while nodes/edges are selected
}
Row events
public function onRowSelect(string $nodeId, string $attrId): void {
// A row attribute was selected on a node
}
public function onRowDeselect(string $nodeId, string $attrId): void {
// A row attribute was deselected
}
public function onRowSelectionChange(array $rows): void {
// Row selection changed
// $rows = ['node1.attr1', 'node2.attr3']
}
Other events
public function onDrop(array $data): void {
// External drag-and-drop onto the canvas
}
public function onInit(array $data): void {
// Canvas finished initializing -- safe to call flow commands
}
Complete example
A component that persists drag positions and handles connections:
<?php
namespace App\Livewire;
use ArtisanFlow\WireFlow\Concerns\WithWireFlow;
use App\Models\FlowNode;
use App\Models\FlowEdge;
use Livewire\Component;
class PersistentFlow extends Component
{
use WithWireFlow;
public array $nodes = [];
public array $edges = [];
public function mount(): void
{
$this->nodes = FlowNode::all()->map(fn ($n) => [
'id' => (string) $n->id,
'position' => ['x' => $n->x, 'y' => $n->y],
'data' => ['label' => $n->label],
])->toArray();
$this->edges = FlowEdge::all()->map(fn ($e) => [
'id' => (string) $e->id,
'source' => (string) $e->source_id,
'target' => (string) $e->target_id,
])->toArray();
}
public function onNodeDragEnd(string $nodeId, array $position): void
{
FlowNode::where('id', $nodeId)->update([
'x' => $position['x'],
'y' => $position['y'],
]);
}
public function onConnect(string $source, string $target, ?string $sourceHandle, ?string $targetHandle): void
{
$edge = FlowEdge::create([
'source_id' => $source,
'target_id' => $target,
]);
$this->edges[] = [
'id' => (string) $edge->id,
'source' => $source,
'target' => $target,
];
}
public function onPaneClick(array $position): void
{
$this->flowDeselectAll();
}
public function render()
{
return view('livewire.persistent-flow');
}
}
{{-- resources/views/livewire/persistent-flow.blade.php --}}
<div>
<x-flow
:nodes="$nodes"
:edges="$edges"
@connect="onConnect"
@node-drag-end="onNodeDragEnd"
@pane-click="onPaneClick"
style="height: 500px;"
>
<x-slot:node>
<x-flow-handle type="target" position="top" />
<span x-text="node.data.label"></span>
<x-flow-handle type="source" position="bottom" />
</x-slot:node>
</x-flow>
</div>
Quick reference
| Event | Handler | Payload |
|---|---|---|
connect |
onConnect |
string $source, string $target, ?string $sourceHandle, ?string $targetHandle |
connect-start |
onConnectStart |
string $source, ?string $sourceHandle |
connect-end |
onConnectEnd |
?array $connection, ?string $source, ?string $sourceHandle, ?array $position |
node-click |
onNodeClick |
string $nodeId, array $node |
node-drag-start |
onNodeDragStart |
string $nodeId |
node-drag-end |
onNodeDragEnd |
string $nodeId, array $position |
node-resize-start |
onNodeResizeStart |
string $nodeId, array $dimensions |
node-resize-end |
onNodeResizeEnd |
string $nodeId, array $dimensions |
node-collapse |
onNodeCollapse |
string $nodeId |
node-expand |
onNodeExpand |
string $nodeId |
node-reparent |
onNodeReparent |
string $nodeId, ?string $newParentId, ?string $oldParentId |
node-context-menu |
onNodeContextMenu |
string $nodeId, array $screenPosition |
nodes-change |
onNodesChange |
array $changes |
edge-click |
onEdgeClick |
string $edgeId |
edge-context-menu |
onEdgeContextMenu |
string $edgeId, array $screenPosition |
edges-change |
onEdgesChange |
array $changes |
reconnect |
onReconnect |
string $oldEdgeId, array $newConnection |
reconnect-start |
onReconnectStart |
string $edgeId, string $handleType |
reconnect-end |
onReconnectEnd |
string $edgeId, bool $successful |
pane-click |
onPaneClick |
array $position |
pane-context-menu |
onPaneContextMenu |
array $position |
viewport-change |
onViewportChange |
array $viewport |
selection-change |
onSelectionChange |
array $nodes, array $edges |
selection-context-menu |
onSelectionContextMenu |
array $nodes, array $edges, array $screenPosition |
row-select |
onRowSelect |
string $nodeId, string $attrId |
row-deselect |
onRowDeselect |
string $nodeId, string $attrId |
row-selection-change |
onRowSelectionChange |
array $rows |
drop |
onDrop |
array $data |
init |
onInit |
array $data |
Related
- WithWireFlow Trait -- setup and base methods
- Convenience Methods -- simplified methods for common operations
- Server Patterns -- complete working patterns