canStartConnection method

ConnectionValidationResult canStartConnection({
  1. required String nodeId,
  2. required String portId,
  3. required bool isOutput,
})

Validates whether a connection can start from the specified port.

This method checks:

  1. Port exists and is connectable
  2. Direction compatibility (output can emit, input can receive)
  3. Max connections not exceeded (for source ports)
  4. Custom validation via ConnectionEvents.onBeforeStart callback

Returns a ConnectionValidationResult indicating if the connection can start.

Implementation

ConnectionValidationResult canStartConnection({
  required String nodeId,
  required String portId,
  required bool isOutput,
}) {
  final node = _nodes[nodeId];
  if (node == null) {
    return const ConnectionValidationResult.deny(reason: 'Node not found');
  }

  final port = node.allPorts.where((p) => p.id == portId).firstOrNull;
  if (port == null) {
    return const ConnectionValidationResult.deny(reason: 'Port not found');
  }

  // Port must be connectable
  if (!port.isConnectable) {
    return const ConnectionValidationResult.deny(
      reason: 'Port is not connectable',
    );
  }

  // Direction check: output ports must be able to emit
  // input ports must be able to receive
  if (isOutput && !port.isOutput) {
    return const ConnectionValidationResult.deny(
      reason: 'Port cannot emit connections',
    );
  }
  if (!isOutput && !port.isInput) {
    return const ConnectionValidationResult.deny(
      reason: 'Port cannot receive connections',
    );
  }

  // Get existing connections for this port
  final existingConnections = _connections
      .where(
        (conn) => isOutput
            ? (conn.sourceNodeId == nodeId && conn.sourcePortId == portId)
            : (conn.targetNodeId == nodeId && conn.targetPortId == portId),
      )
      .map((c) => c.id)
      .toList();

  // Check max connections for source port (output side)
  // Note: For input ports starting a drag, they become the target,
  // so we check source (output) max connections
  if (isOutput && port.maxConnections != null) {
    // If port doesn't allow multi-connections, we'll remove existing on drag
    // So only block if multi-connections is allowed but max is reached
    if (port.multiConnections &&
        existingConnections.length >= port.maxConnections!) {
      return const ConnectionValidationResult.deny(
        reason: 'Maximum connections reached',
      );
    }
  }

  // Call custom validation callback if provided
  final onBeforeStart = events.connection?.onBeforeStart;
  if (onBeforeStart != null) {
    final context = ConnectionStartContext<T>(
      sourceNode: node,
      sourcePort: port,
      existingConnections: existingConnections,
    );
    final result = onBeforeStart(context);
    if (!result.allowed) {
      return result;
    }
  }

  return const ConnectionValidationResult.allow();
}