PHP code example of codenzia / filament-diagrammer

1. Go to this page and download the library: Download codenzia/filament-diagrammer library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

codenzia / filament-diagrammer example snippets




declare(strict_types=1);

namespace App\Filament\Pages;

use Codenzia\FilamentDiagrammer\Components\DiagramCanvas;
use Codenzia\FilamentDiagrammer\Components\DiagramConnection;
use Codenzia\FilamentDiagrammer\Components\DiagramNode;
use Codenzia\FilamentDiagrammer\Enums\ConnectionEndpoint;
use Codenzia\FilamentDiagrammer\Enums\ConnectorType;
use Codenzia\FilamentDiagrammer\Pages\Diagram;

class MyDiagram extends Diagram
{
    protected static ?string $title = 'My Diagram';

    protected function getDiagramCanvas(): DiagramCanvas
    {
        return DiagramCanvas::make()
            ->grid(enabled: true, size: 20, type: 'dots')
            ->zoom(enabled: true, min: 0.1, max: 3.0, step: 0.05)
            ->snapToGrid()
            ->defaultConnector(ConnectorType::BEZIER)
            ->nodes($this->getNodes())
            ->connections($this->getConnections());
    }

    protected function getNodes(): array
    {
        return [
            DiagramNode::make('start')
                ->label('Start')
                ->type('Process')
                ->position(100, 100)
                ->color('#22c55e')
                ->sourceEndpoints([ConnectionEndpoint::BOTTOM]),

            DiagramNode::make('end')
                ->label('End')
                ->type('Process')
                ->position(400, 300)
                ->color('#3b82f6')
                ->targetEndpoints([ConnectionEndpoint::TOP]),
        ];
    }

    protected function getConnections(): array
    {
        return [
            DiagramConnection::make('start', 'end')
                ->label('Flow')
                ->connector(ConnectorType::BEZIER)
                ->animated(),
        ];
    }
}



declare(strict_types=1);

namespace App\Diagram\NodeTypes;

use Codenzia\FilamentDiagrammer\Enums\NodeShape;
use Codenzia\FilamentDiagrammer\NodeTypes\BaseNodeType;
use Filament\Forms\Components\TagsInput;
use Filament\Forms\Components\Textarea;
use Filament\Forms\Components\TextInput;
use Filament\Infolists\Components\TextEntry;

class GoalNode extends BaseNodeType
{
    public static function label(): string
    {
        return 'Goal';
    }

    public static function icon(): ?string
    {
        return 'heroicon-o-flag';
    }

    public static function defaultColor(): ?string
    {
        return '#22c55e';
    }

    public static function defaultShape(): NodeShape
    {
        return NodeShape::RECTANGLE;
    }

    // Filament schema components for the body section
    public static function bodySchema(): array
    {
        return [
            TextEntry::make('description')
                ->hiddenLabel()
                ->size('xs')
                ->color('gray'),
        ];
    }

    // Filament form components for the edit slide-over
    public static function editFormSchema(): array
    {
        return [
            TextInput::make('label')->label('Title')->

use App\Diagram\NodeTypes\GoalNode;

DiagramNode::make('goal-1')
    ->label('Customer Experience')
    ->type(GoalNode::class)        // Schemas resolved from class
    ->position(100, 200)
    ->data([
        'description' => 'Improve NPS score by 20 points',
        'tags' => ['Customer', 'CX'],
        'progress' => 60.0,
    ]);

use App\Models\Kpi;
use App\Diagram\NodeTypes\KpiNode;

protected function getNodes(): array
{
    return Kpi::all()->map(fn (Kpi $kpi) =>
        DiagramNode::make("kpi-{$kpi->id}")
            ->label($kpi->title)
            ->type(KpiNode::class)
            ->record($kpi)                    // Components resolve from model attributes
    )->toArray();
}

public static function bodySchema(): array
{
    return [
        TextEntry::make('description'),       // Reads $kpi->description
        TagEntry::make('tags'),               // Reads $kpi->tags (relationship or cast)
    ];
}

DiagramNode::make("kpi-{$kpi->id}")
    ->record($kpi)
    ->data(['progress' => 100.0]);            // Overrides $kpi->progress

DiagramNode::make('custom-1')
    ->label('Custom Node')
    ->type('Task')
    ->bodySchema([
        TextEntry::make('description')->hiddenLabel(),
    ])
    ->footerSchema([
        ProgressBarEntry::make('progress')->hiddenLabel(),
    ])
    ->editFormSchema([
        TextInput::make('label')->label('Title')->

use Codenzia\FilamentDiagrammer\Components\DiagramCanvas;
use Codenzia\FilamentDiagrammer\Components\DiagramConnection;
use Codenzia\FilamentDiagrammer\Components\DiagramNode;
use Codenzia\FilamentDiagrammer\Enums\ConnectionEndpoint;
use Codenzia\FilamentDiagrammer\Enums\ConnectorType;
use Codenzia\FilamentDiagrammer\Enums\HeaderStyle;
use Filament\Forms\Components\TagsInput;
use Filament\Forms\Components\TextInput;
use Filament\Infolists\Components\TextEntry;

protected function getDiagramCanvas(): DiagramCanvas
{
    return DiagramCanvas::make()
        ->snapToGrid()
        ->defaultConnector(ConnectorType::STRAIGHT)
        ->nodes($this->getClassNodes())
        ->connections($this->getClassConnections());
}

protected function getClassNodes(): array
{
    // Pre-compute display HTML to avoid Filament TextEntry array-state limitations
    $fmtProps = fn (array $props): string => collect($props)
        ->map(fn ($p) => '<div class="font-mono py-0.5 text-gray-700 dark:text-gray-300">'.e($p).'</div>')
        ->join('');

    $fmtMethods = fn (array $methods): string => collect($methods)
        ->map(fn ($m) => '<div class="font-mono py-0.5 text-violet-600 dark:text-violet-400">'.e($m).'</div>')
        ->join('');

    $makeNode = fn (string $id, string $label, array $properties, array $methods, float $x, float $y) =>
        DiagramNode::make($id)
            ->label($label)
            ->type('Class')
            ->color('#3b82f6')
            ->icon('heroicon-o-code-bracket')
            ->headerStyle(HeaderStyle::MAC)
            ->position($x, $y)
            ->size(260)
            ->sourceEndpoints([ConnectionEndpoint::RIGHT, ConnectionEndpoint::BOTTOM])
            ->targetEndpoints([ConnectionEndpoint::LEFT, ConnectionEndpoint::TOP])
            ->bodySchema([
                TextEntry::make('properties_html')->hiddenLabel()->html()->size('xs'),
            ])
            ->footerSchema([
                TextEntry::make('methods_html')->hiddenLabel()->html()->size('xs'),
            ])
            ->editFormSchema([
                TextInput::make('label')->label('Class Name')->

DiagramCanvas::make()
    // ─── Layout ───────────────────────────────────────────────
    ->height('70vh')
    ->background('#f8fafc')                   // Light mode background (null = auto)
    ->canvasDarkBackground('#1a1b1e')         // Dark mode background (null = auto)

    // ─── Grid ─────────────────────────────────────────────────
    ->grid(enabled: true, size: 20, color: null, type: 'dots')
    ->gridDarkColor('#2a2b2e')                // Grid color for dark mode
    ->snapToGrid(true)

    // ─── Zoom & Pan ──────────────────────────────────────────
    ->zoom(enabled: true, min: 0.1, max: 3.0, step: 0.05, default: 1.0)
    ->panEnabled(true)

    // ─── Toolbar ─────────────────────────────────────────────
    ->toolbar(enabled: true, position: 'top')
    ->undoRedo(true)                          // Undo/redo buttons (Ctrl+Z / Ctrl+Y)
    ->save(true)                              // Save button (Ctrl+S) with dirty indicator
    ->export(true)                            // Export dropdown (PNG/PDF with theme choice)
    ->zoomControls(true)
    ->zoomControlsPosition(ZoomControlsPosition::BOTTOM_RIGHT)  // TOP_LEFT, TOP_RIGHT, BOTTOM_LEFT, BOTTOM_RIGHT, FLOAT, DOCKED
    ->fitView(true)
    ->fitViewPadding(60)                      // Padding around nodes when fitting

    // ─── Interactivity ───────────────────────────────────────
    ->readOnly(false)
    ->keyboardShortcuts(true)
    ->multiSelect(true)
    ->interactionModeToggle(true)             // Show pan/select mode toggle in toolbar
    ->defaultInteractionMode('pan')           // 'pan' or 'select' — default left-click drag behavior
    ->dragToConnect()                          // Enable drag port-to-port connections
    ->newConnectionDefaults([...])            // Default props for new connections
    ->autoOpenConnectionEditor()              // Open editor after new connection
    ->defaultConnector(ConnectorType::BEZIER)

    // ─── Context Menu ────────────────────────────────────────
    ->contextMenu(true)                       // Right-click context menu
    ->contextMenuItems([                      // Custom menu items
        ContextMenuItem::make('export-node')
            ->label('Export as JSON')
            ->icon('heroicon-o-arrow-down-tray')
            ->group('node')
            ->wireMethod('exportNodeAsJson'),
    ])

    // ─── Alignment Guides ────────────────────────────────────
    ->alignmentGuides(true)                   // Snap-to-alignment guides during drag
    ->alignmentThreshold(5)                   // Snap distance in pixels

    // ─── Minimap ─────────────────────────────────────────────
    ->minimap(true)                           // Show minimap overview
    ->minimapPosition('bottom-right')         // bottom-right, bottom-left, top-right, top-left

    // ─── Connection Styling ──────────────────────────────────
    ->connectionColor('#9ca3af')              // Light mode stroke
    ->connectionDarkColor('#6b7280')          // Dark mode stroke
    ->connectionHoverColor('#f59e0b')         // Hover highlight
    ->connectionHoverWidth(3)                 // Stroke width on hover
    ->connectionArrowSize(10)                 // Arrow head size
    ->connectionArrowFoldback(0.8)            // 0.0 = flat, 1.0 = sharp point

    // ─── Endpoints ───────────────────────────────────────────
    ->endpointType('Dot')                     // 'Dot', 'Rectangle', or 'Blank'
    ->endpointRadius(2)

    // ─── Selection ───────────────────────────────────────────
    ->selectionColor('#f59e0b')               // Border/glow color for selected nodes

    // ─── Node Defaults ───────────────────────────────────────
    ->nodeDefaultWidth(240)
    ->nodeDefaultHeight(200)                   // null = auto (content-driven)
    ->contentSizing('auto')                    // 'auto', 'fit', or 'clip'

    // ─── Auto Layout ─────────────────────────────────────────
    ->autoLayoutHGap(60)                      // Horizontal gap between node edges (px)
    ->autoLayoutVGap(60)                      // Vertical gap between levels (px)

    // ─── Data ────────────────────────────────────────────────
    ->nodes([...])
    ->connections([...])

    // ─── Actions (Filament Actions) ──────────────────────────
    ->headerActions([...])
    ->toolbarActions([...])
    ->footerActions([...])

    // ─── Lifecycle Callbacks ─────────────────────────────────
    ->onNodeCreate(fn ($nodeId) => ...)
    ->onNodeDelete(fn ($nodeId) => ...)
    ->onNodeMove(fn ($nodeId, $x, $y) => ...)
    ->onConnectionCreate(fn ($sourceId, $targetId) => ...)
    ->onConnectionDelete(fn ($sourceId, $targetId) => ...)
    ->onSave(fn ($nodes, $connections) => ...);

DiagramNode::make('node-1')
    // ─── Basic ───────────────────────────────────────────────
    ->label('My Node')
    ->type(GoalNode::class)                   // NodeType class or plain string
    ->position(100, 200)                      // Optional — auto-layout if omitted
    ->size(width: 240, height: null)          // Height auto-sizes by default
    ->autoFit()                               // Width adapts to content
    ->shape(NodeShape::RECTANGLE)
    ->color('#3b82f6')                        // Accent color (header tint)
    ->borderColor('#e5e7eb')
    ->icon('heroicon-o-bolt')

    // ─── Header Styling ──────────────────────────────────────
    ->headerBackgroundColor('#1e40af')        // Custom header background
    ->headerStyle(HeaderStyle::MAC)           // none, windows, mac, custom
    ->footerBackgroundColor('#f1f5f9')        // Custom footer background

    // ─── Behavior ────────────────────────────────────────────
    ->draggable(true)                         // Can user drag this node?
    ->selectable(true)                        // Can user click to select?
    ->deletable(true)                         // Show X button & allow Delete key?
    ->connectable(true)                       // Show connection endpoints?
    ->resizable(false)                        // Allow resize handles?

    // ─── Connection Endpoints ────────────────────────────────
    ->sourceEndpoints([ConnectionEndpoint::BOTTOM, ConnectionEndpoint::RIGHT])
    ->targetEndpoints([ConnectionEndpoint::TOP, ConnectionEndpoint::LEFT])

    // ─── Sections (Filament Schemas) ─────────────────────────
    ->headerSchema([...])                     // Or resolved from NodeType
    ->bodySchema([...])
    ->footerSchema([...])
    ->leftSidebarSchema([...])
    ->rightSidebarSchema([...])
    ->editFormSchema([...])                   // Edit slide-over form (inline, no NodeType needed)

    // ─── Section Actions ─────────────────────────────────────
    ->headerActions([...])
    ->footerActions([...])

    // ─── Data (backing state for schemas) ────────────────────
    ->record($eloquentModel)                  // Bind an Eloquent model directly
    ->data([                                  // Or provide data manually (overrides record)
        'description' => 'Some description',
        'tags' => ['Tag1', 'Tag2'],
        'progress' => 75.0,
    ])

    // ─── Callbacks ───────────────────────────────────────────
    ->onClick(fn ($nodeId) => ...)
    ->onDoubleClick(fn ($nodeId) => ...)

    // ─── Extra HTML Attributes ───────────────────────────────
    ->extraAttributes(['data-priority' => 'high']);

// These nodes will be auto-positioned
DiagramNode::make('a')->label('A')->type(GoalNode::class),
DiagramNode::make('b')->label('B')->type(GoalNode::class),
DiagramNode::make('c')->label('C')->type(GoalNode::class),

DiagramNode::make('flexible')
    ->label('Auto-sized')
    ->autoFit();

DiagramNode::make('styled')
    ->headerStyle(HeaderStyle::MAC)
    ->headerBackgroundColor('#1e293b');

// "Fit" mode: width × height define content area, circle grows to contain it
DiagramCanvas::make()
    ->nodeDefaultWidth(240)
    ->nodeDefaultHeight(200)
    ->contentSizing('fit')    // Circle diameter = √(240² + 200²) ≈ 313px
    ->nodes([
        DiagramNode::make('process')
            ->label('My Process')
            ->shape(NodeShape::CIRCLE),
    ]);

DiagramConnection::make('source-node-id', 'target-node-id')
    // ─── Basic ───────────────────────────────────────────────
    ->label('100%')
    ->value(100)                              // Arbitrary value for callbacks
    ->connector(ConnectorType::BEZIER)

    // ─── Styling ─────────────────────────────────────────────
    ->color('#f59e0b')                        // Stroke color
    ->width(2)                                // Stroke width
    ->animated(true)                          // Dashed flowing animation
    ->hoverColor('#ef4444')                   // Per-connection hover color

    // ─── Arrows ──────────────────────────────────────────────
    ->arrowSize(10)                           // Arrow head size
    ->arrowFoldback(0.8)                      // 0.0 = flat, 1.0 = sharp point

    // ─── Label ───────────────────────────────────────────────
    ->labelPosition(0.5)                      // 0.0 = at source, 1.0 = at target

    // ─── Endpoints ───────────────────────────────────────────
    ->sourceEndpoint(ConnectionEndpoint::BOTTOM)
    ->targetEndpoint(ConnectionEndpoint::TOP)

    // ─── Behavior ────────────────────────────────────────────
    ->deletable(true)
    ->editable(true)                          // Allow label editing via double-click

    // ─── Custom Data ─────────────────────────────────────────
    ->data(['weight' => 0.5])
    ->extraAttributes(['data-type' => 'dependency'])

    // ─── Callbacks ───────────────────────────────────────────
    ->onConnect(fn ($sourceNode, $targetNode) => ...)
    ->onDisconnect(fn ($sourceNode, $targetNode) => ...)
    ->onClick(fn ($connection) => ...);

use Codenzia\FilamentDiagrammer\Contracts\NodeType;
use Codenzia\FilamentDiagrammer\Enums\NodeShape;

interface NodeType
{
    public static function label(): string;
    public static function description(): ?string;  // Shown in palette help tooltip
    public static function icon(): ?string;
    public static function defaultColor(): ?string;
    public static function defaultShape(): NodeShape;
    public static function defaultWidth(): ?float;
    public static function headerSchema(): array;
    public static function bodySchema(): array;
    public static function footerSchema(): array;
    public static function leftSidebarSchema(): array;
    public static function rightSidebarSchema(): array;
    public static function editFormSchema(): array;
}

use Codenzia\FilamentDiagrammer\Concerns\HasDiagram;

class MyPage extends Page implements HasActions, HasForms
{
    use HasDiagram;
    use InteractsWithActions;
    use InteractsWithForms;

    // Required: define the canvas configuration
    protected function getDiagramCanvas(): DiagramCanvas { ... }

    // Required for an editable diagram: the plugin fails closed, so without
    // this override every diagram mutation is rejected with a 403
    protected function canEditDiagram(): bool
    {
        return auth()->user()?->can('update', $this->record) ?? false;
    }

    // Optional: customize the node edit form (opens in slide-over)
    // Note: if a node has a NodeType with editFormSchema(), that is used instead
    protected function getNodeFormSchema(): array
    {
        return [
            TextInput::make('title')->el
    }
}

DiagramCanvas::make()
    ->background('#ffffff')                   // Light mode
    ->canvasDarkBackground('#0f172a')         // Dark mode
    ->gridColor('#e5e7eb')                    // Light grid
    ->gridDarkColor('#1e3a5f');               // Dark grid

DiagramCanvas::make()
    ->undoRedo(true);   // Enabled by default

DiagramCanvas::make()
    ->save(true)        // Enabled by default
    ->onSave(fn (array $nodes, array $connections) => /* persist to DB */);

// Eloquent model with a JSON column
->onSave(fn ($nodes, $connections) => $this->diagram->update([
    'state' => ['nodes' => $nodes, 'connections' => $connections],
]))

// Simple JSON file (suitable for prototypes / demos)
->onSave(fn ($nodes, $connections) => file_put_contents(
    storage_path('app/diagram.json'),
    json_encode(compact('nodes', 'connections'))
))

DiagramCanvas::make()
    ->export(true);     // Enabled by default

protected function getConnectionFormSchema(): array
{
    return [
        TextInput::make('label'),
        TextInput::make('value')->numeric(),
        Select::make('connector')->options([
            'bezier' => 'Bezier',
            'straight' => 'Straight',
            'flowchart' => 'Flowchart',
        ]),
    ];
}

use Codenzia\FilamentDiagrammer\Enums\ZoomControlsPosition;

DiagramCanvas::make()
    ->zoomControlsPosition(ZoomControlsPosition::BOTTOM_RIGHT);  // Default

DiagramCanvas::make()
    ->contextMenu(true)  // Enabled by default

use Codenzia\FilamentDiagrammer\Components\ContextMenuItem;

DiagramCanvas::make()
    ->contextMenuItems([
        ContextMenuItem::make('export-json')
            ->label('Export as JSON')
            ->icon('heroicon-o-arrow-down-tray')
            ->group('node')                    // 'node', 'connection', or 'canvas'
            ->wireMethod('exportNodeAsJson')   // Livewire method to call
            ->shortcut('Ctrl+E')
            ->sort(50),

        ContextMenuItem::make('highlight')
            ->label('Highlight Node')
            ->group('node')
            ->wireMethod('highlightNode'),
    ]);

DiagramCanvas::make()
    ->multiSelect(true)

DiagramCanvas::make()
    ->interactionModeToggle(true)         // Show the toggle (default: true)
    ->defaultInteractionMode('pan')       // 'pan' or 'select' (default: 'pan')

DiagramCanvas::make()
    ->alignmentGuides(true)     // Disabled by default
    ->alignmentThreshold(5)     // Snap distance in pixels (default: 5)

DiagramCanvas::make()
    ->minimap(true)                            // Disabled by default
    ->minimapPosition('bottom-right')          // bottom-right, bottom-left, top-right, top-left

DiagramCanvas::make()
    ->dragToConnect()           // Disabled by default

DiagramNode::make('locked-node')
    ->connectable(false)        // No connection ports shown

// Static defaults
DiagramCanvas::make()
    ->newConnectionDefaults([
        'connector' => 'bezier',
        'color' => '#6b7280',
        'width' => 2,
        'animated' => false,
        'arrowSize' => 10,
        'arrowFoldback' => 0.8,
        'label' => null,
        'value' => null,
    ]);

// Dynamic defaults via Closure (resolved server-side per connection)
DiagramCanvas::make()
    ->newConnectionDefaults(fn (string $sourceId, string $targetId) => [
        'connector' => 'bezier',
        'color' => str_starts_with($sourceId, 'critical') ? '#ef4444' : '#6b7280',
        'label' => "Link: {$sourceId} → {$targetId}",
        'animated' => true,
    ]);

DiagramCanvas::make()
    ->autoOpenConnectionEditor()
    ->newConnectionDefaults([
        'connector' => 'bezier',
    ]);

protected function getConnectionFormSchema(): array
{
    return [
        \Filament\Forms\Components\Slider::make('value')
            ->label('Contribution %')
            ->range(0, 100)
            ->step(5)
            ->tooltips(),
    ];
}

DiagramConnection::make('a', 'b')
    ->label('Relationship')
    ->editable(true)            // Allow double-click editing (default: true)

DiagramCanvas::make()->keyboardShortcuts(false);

return [
    'assets' => [
        'css' => ['css/filament-diagrammer.css'],
        'js' => ['js/filament-diagrammer.js'],
    ],

    'canvas' => [
        'height' => '80vh',
        'background' => null,               // null = auto (follows theme)
        'grid' => [
            'enabled' => true,
            'size' => 20,
            'color' => null,                // null = auto
            'type' => 'dots',               // dots, lines, cross
        ],
        'zoom' => [
            'enabled' => true,
            'min' => 0.1,
            'max' => 3.0,
            'step' => 0.05,
            'default' => 1.0,
        ],
        'pan' => ['enabled' => true],
        'snap_to_grid' => true,
    ],

    'node' => [
        'width' => 240,
        'min_width' => 120,
        'max_width' => 600,
        'shape' => 'rectangle',
        'draggable' => true,
        'selectable' => true,
        'resizable' => false,
        'deletable' => true,
    ],

    'connection' => [
        'connector' => 'bezier',
        'color' => '#6b7280',
        'width' => 2,
        'animated' => false,
        'deletable' => true,
        'arrow_size' => 8,
        'label_position' => 0.5,
    ],

    'toolbar' => [
        'enabled' => true,
        'position' => 'top',
        'undo_redo' => true,
        'save' => true,
        'export' => true,
        'zoom_controls' => true,
        'zoom_controls_position' => 'bottom-right',  // top-left, top-right, bottom-left, bottom-right, float, docked
        'fit_view' => true,
        'minimap' => false,
    ],

    'read_only' => false,
    'keyboard_shortcuts' => true,
    'multi_select' => true,
    'drag_to_connect' => false,             // drag port-to-port connections
    'auto_open_connection_editor' => false,  // open editor after new connection
    'new_connection_defaults' => [],         // default props for new connections
    'context_menu' => true,
    'alignment_guides' => false,
    'alignment_threshold' => 5,             // px

    'limits' => [
        'max_nodes' => 500,                 // upper bound accepted from a full-graph save
        'max_connections' => 2000,
    ],
];

class ConditionNodeType extends BaseNodeType
{
    public static function outputPorts(): array
    {
        return [
            ['id' => 'yes', 'label' => 'Yes', 'position' => 'BOTTOM_LEFT', 'color' => '#22c55e'],
            ['id' => 'no', 'label' => 'No', 'position' => 'BOTTOM_RIGHT', 'color' => '#ef4444'],
        ];
    }
}

$node = DiagramNode::make('condition-1')
    ->type(ConditionNodeType::class)
    ->outputPorts([
        ['id' => 'yes', 'label' => 'Yes', 'position' => 'BOTTOM_LEFT', 'color' => '#22c55e'],
        ['id' => 'no', 'label' => 'No', 'position' => 'BOTTOM_RIGHT', 'color' => '#ef4444'],
    ]);

$connection = DiagramConnection::make('node-1', 'node-2')
    ->sourcePort('yes')
    ->label('Yes');

$canvas = DiagramCanvas::make()
    ->beforeConnectionCreate(function (string $sourceId, string $targetId, ?string $sourcePort): bool {
        // Return false to reject the connection
        if ($sourceId === $targetId) {
            return false;
        }
        return true;
    });

class TriggerNodeType extends BaseNodeType
{
    // Only accept connections from these node types
    public static function allowedInputTypes(): array
    {
        return []; // empty = accept any (default)
    }

    // Only allow connections to these node types
    public static function allowedOutputTypes(): array
    {
        return [ConditionNodeType::class, ActionNodeType::class];
    }

    // Limit the number of incoming connections
    public static function maxInputConnections(): ?int
    {
        return 0; // trigger nodes have no inputs
    }

    // Limit the number of outgoing connections
    public static function maxOutputConnections(): ?int
    {
        return null; // null = unlimited
    }
}

$canvas = DiagramCanvas::make()
    ->palette([
        TriggerNodeType::class,
        ConditionNodeType::class,
        ActionNodeType::class,
    ]);

$canvas = DiagramCanvas::make()
    ->palette(nodeTypes: [...], position: 'right');
bash
php artisan vendor:publish --tag=filament-diagrammer-config
bash
php artisan vendor:publish --tag=filament-diagrammer-assets
css
/* resources/css/filament/{panel}/theme.css */
@source '../../../../vendor/codenzia/*/src/**/*.php';
@source '../../../../vendor/codenzia/*/resources/views/**/*.blade.php';
blade
@php
    $canvasConfig = $this->diagramCanvasConfig;
    $nodes = $this->diagramNodes;
    $connections = $this->diagramConnections;
@endphp

@
js
// Export as PNG with current theme
exportAs('png', 'current');

// Export as PDF in light mode
exportAs('pdf', 'light');

// Export as PNG in dark mode
exportAs('png', 'dark');