I can link items by hovering
This commit is contained in:
@@ -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); }
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user