updateConnectionDrag method

void updateConnectionDrag({
  1. required Offset graphPosition,
  2. String? targetNodeId,
  3. String? targetPortId,
  4. Rect? targetNodeBounds,
})

Updates a connection drag with the current position.

Call this from PortWidget's GestureDetector.onPanUpdate.

Parameters:

  • graphPosition: Current pointer position in graph coordinates
  • targetNodeId: ID of node being hovered (null if none)
  • targetPortId: ID of port being hovered (null if none)
  • targetNodeBounds: Bounds of hovered node (null if none)

Implementation

void updateConnectionDrag({
  required Offset graphPosition,
  String? targetNodeId,
  String? targetPortId,
  Rect? targetNodeBounds,
}) {
  final temp = interaction.temporaryConnection.value;
  if (temp == null) return;

  // Validate target port before highlighting
  // Only highlight valid connection targets
  // Skip custom validation during drag for performance - full validation runs at completion
  final isValidTarget =
      targetNodeId != null &&
      targetPortId != null &&
      canConnect(
        targetNodeId: targetNodeId,
        targetPortId: targetPortId,
        skipCustomValidation: true,
      ).allowed;

  // If target is invalid, treat as no target
  final validTargetNodeId = isValidTarget ? targetNodeId : null;
  final validTargetPortId = isValidTarget ? targetPortId : null;
  final validTargetBounds = isValidTarget ? targetNodeBounds : null;

  runInAction(() {
    // Handle port highlighting when target port changes
    final prevNodeId = temp.targetNodeId;
    final prevPortId = temp.targetPortId;
    final targetChanged =
        prevNodeId != validTargetNodeId || prevPortId != validTargetPortId;

    if (targetChanged) {
      // Reset previous port's highlighted state
      if (prevNodeId != null && prevPortId != null) {
        final prevNode = _nodes[prevNodeId];
        if (prevNode != null) {
          final prevPort = prevNode.allPorts
              .where((p) => p.id == prevPortId)
              .firstOrNull;
          prevPort?.highlighted.value = false;
        }
      }

      // Set new port's highlighted state (only if valid target)
      if (validTargetNodeId != null && validTargetPortId != null) {
        final newNode = _nodes[validTargetNodeId];
        if (newNode != null) {
          final newPort = newNode.allPorts
              .where((p) => p.id == validTargetPortId)
              .firstOrNull;
          newPort?.highlighted.value = true;
        }
      }
    }

    // Update connection endpoint - snap to target port if we have a valid one
    if (validTargetNodeId != null && validTargetPortId != null) {
      // Snap to the target port's connection point
      final targetNode = _nodes[validTargetNodeId];
      if (targetNode != null) {
        final targetPort = targetNode.allPorts
            .where((p) => p.id == validTargetPortId)
            .firstOrNull;
        if (targetPort != null) {
          // Calculate the actual connection point on the target port
          assert(
            _theme != null,
            'Theme must be set for connection operations',
          );
          final effectivePortSize = _theme!.portTheme.resolveSize(targetPort);
          final snapPoint = targetNode.getConnectionPoint(
            validTargetPortId,
            portSize: effectivePortSize,
            shape: nodeShapeBuilder?.call(targetNode),
          );
          temp.currentPoint = snapPoint;
        } else {
          temp.currentPoint = graphPosition;
        }
      } else {
        temp.currentPoint = graphPosition;
      }
    } else {
      // No valid target - follow mouse
      temp.currentPoint = graphPosition;
    }

    if (temp.targetNodeId != validTargetNodeId) {
      temp.targetNodeId = validTargetNodeId;
    }
    if (temp.targetPortId != validTargetPortId) {
      temp.targetPortId = validTargetPortId;
    }
    if (temp.targetNodeBounds != validTargetBounds) {
      temp.targetNodeBounds = validTargetBounds;
    }
  });
}