I can link items by hovering

This commit is contained in:
2025-07-02 20:55:06 +02:00
parent f4e8f7a16c
commit d90613119f
2 changed files with 108 additions and 12 deletions

View File

@@ -32,6 +32,10 @@
box-shadow: 0 0 10px rgba(239, 68, 68, 0.3);
}
.wkf-workflow-component.dragging {
transition: none;
}
.wkf-connection-line {
position: absolute;
pointer-events: none;
@@ -47,8 +51,19 @@
cursor: crosshair;
border: 2px solid white;
box-shadow: 0 2px 4px rgba(0,0,0,0.2);
transition: background-color 0.2s, transform 0.2s;
}
.wkf-connection-point.potential-connection {
box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.5);
animation: pulse 0.7s infinite;
}
.wkf-connection-point.potential-start {
background: #ef4444;
}
.wkf-output-point {
right: -6px;
top: 50%;
@@ -65,3 +80,8 @@
background: #ef4444;
transform: translateY(-50%) scale(1.2);
}
@keyframes pulse {
0% { box-shadow: 0 0 0 0 rgba(59, 130, 246, 0.7); }
70% { box-shadow: 0 0 0 6px rgba(59, 130, 246, 0); }
100% { box-shadow: 0 0 0 0 rgba(59, 130, 246, 0); }
}

View File

@@ -4,8 +4,8 @@ function bindWorkflowDesigner(elementId) {
draggedType: null,
draggedComponent: null,
selectedComponent: null,
connectionMode: false,
connectionStart: null
connectionStart: null,
potentialConnectionStart: null,
};
// Get the designer container and canvas
@@ -25,8 +25,24 @@ function bindWorkflowDesigner(elementId) {
// Handle components
if (event.target.closest('.wkf-workflow-component')) {
designer.draggedComponent = event.target.closest('.wkf-workflow-component').dataset.componentId;
const component = event.target.closest('.wkf-workflow-component');
component.classList.add('dragging');
designer.draggedComponent = component.dataset.componentId;
event.dataTransfer.effectAllowed = 'move';
// Create an invisible image to use as the drag image
const invisibleImg = new Image();
invisibleImg.src = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7'; // 1px transparent GIF
event.dataTransfer.setDragImage(invisibleImg, 0, 0);
// Highlight all valid output points on other components
designerContainer.querySelectorAll('.wkf-connection-point').forEach(point => {
if (point.dataset.pointType === 'output' &&
point.dataset.componentId !== designer.draggedComponent) {
point.classList.add('potential-connection');
}
});
}
});
@@ -40,13 +56,44 @@ function bindWorkflowDesigner(elementId) {
const y = event.clientY - rect.top - 40;
component.style.left = Math.max(0, x) + 'px';
component.style.top = Math.max(0, y) + 'px';
const componentRect = component.getBoundingClientRect();
const componentId = component.dataset.componentId;
const outputPoints = designerContainer.querySelectorAll('.wkf-connection-point[data-point-type="output"]');
outputPoints.forEach(point => {
if (point.dataset.componentId === componentId) return; // Skip points from the same component
const pointRect = point.getBoundingClientRect();
const pointCircle = {
x: pointRect.left + pointRect.width / 2,
y: pointRect.top + pointRect.height / 2,
radius: 6
};
if (point != designer.potentialConnectionStart && _isOverlapping(componentRect, pointCircle)) {
console.debug("overlapping !")
outputPoints.forEach(other_point => {
other_point.classList.remove("potential-start")
});
designer.potentialConnectionStart = point.dataset.componentId;
point.classList.add('potential-start');
}
});
// Update connections in real-time
updateConnections(designerContainer);
});
designerContainer.addEventListener('dragend', (event) => {
if (!event.target.closest('.wkf-workflow-component')) return;
if (designer.draggedComponent) {
if (designer.draggedComponent) {
const component = event.target.closest('.wkf-workflow-component');
const draggedComponentId = component.dataset.componentId;
component.classList.remove('dragging');
const rect = canvas.getBoundingClientRect();
const x = event.clientX - rect.left - 64;
const y = event.clientY - rect.top - 40;
@@ -61,7 +108,30 @@ function bindWorkflowDesigner(elementId) {
y: Math.max(0, y),
}
});
// Create connection if we ended the drag over a connection point
if (designer.potentialConnectionStart) {
htmx.ajax('POST', '/workflows/add-connection', {
target: `#c_${elementId}`,
headers: {"Content-Type": "application/x-www-form-urlencoded"},
swap: "innerHTML",
values: {
_id: elementId,
from_id: designer.potentialConnectionStart, // The output point we're hovering over
to_id: draggedComponentId, // The component we're dragging
}
});
}
// Remove highlighting from all connection points
designerContainer.querySelectorAll('.wkf-connection-point').forEach(point => {
point.classList.remove('potential-connection');
});
designer.draggedComponent = null;
designer.potentialConnectionStart = null;
// Update connections after drag ends
updateConnections(designerContainer);
}
@@ -217,10 +287,16 @@ function bindWorkflowDesigner(elementId) {
return designer;
}
// Initialize all workflow designers on page load
document.addEventListener('DOMContentLoaded', () => {
// Find all workflow designer containers and bind them
document.querySelectorAll('[id^="wkf-designer-"]').forEach(designer => {
bindDesigner(designer.id);
});
});
function _isOverlapping(rect, circle) {
// Find the closest point on the rectangle to the circle's center
const closestX = Math.max(rect.x, Math.min(circle.x, rect.x + rect.width));
const closestY = Math.max(rect.y, Math.min(circle.y, rect.y + rect.height));
// Calculate the distance between the circle's center and the closest point
const deltaX = circle.x - closestX;
const deltaY = circle.y - closestY;
const distanceSquared = deltaX * deltaX + deltaY * deltaY;
// Check if the distance is less than or equal to the circle's radius
return distanceSquared <= circle.radius * circle.radius;
}