|
|
|
@ -30,8 +30,58 @@ interface GroupBackgroundProps { |
|
|
|
|
|
|
|
// Renders just the group background - displayed below nodes (z-index 1)
|
|
|
|
function GroupBackground({ groupId }: GroupBackgroundProps) { |
|
|
|
const { groups } = useWorkflowStore(); |
|
|
|
const { groups, updateGroup, moveGroupNodes } = useWorkflowStore(); |
|
|
|
const group = groups[groupId]; |
|
|
|
const { zoom } = useViewport(); |
|
|
|
const [isDragging, setIsDragging] = useState(false); |
|
|
|
const dragStartRef = useRef<{ x: number; y: number } | null>(null); |
|
|
|
|
|
|
|
const handleBackgroundMouseDown = useCallback( |
|
|
|
(e: React.MouseEvent) => { |
|
|
|
if (e.button !== 0) return; |
|
|
|
|
|
|
|
e.stopPropagation(); |
|
|
|
e.preventDefault(); |
|
|
|
setIsDragging(true); |
|
|
|
dragStartRef.current = { x: e.clientX, y: e.clientY }; |
|
|
|
}, |
|
|
|
[] |
|
|
|
); |
|
|
|
|
|
|
|
useEffect(() => { |
|
|
|
if (!isDragging || !group) return; |
|
|
|
|
|
|
|
const handleMouseMove = (e: MouseEvent) => { |
|
|
|
if (!dragStartRef.current) return; |
|
|
|
|
|
|
|
const deltaX = (e.clientX - dragStartRef.current.x) / zoom; |
|
|
|
const deltaY = (e.clientY - dragStartRef.current.y) / zoom; |
|
|
|
|
|
|
|
if (Math.abs(deltaX) > 2 || Math.abs(deltaY) > 2) { |
|
|
|
updateGroup(groupId, { |
|
|
|
position: { |
|
|
|
x: group.position.x + deltaX, |
|
|
|
y: group.position.y + deltaY, |
|
|
|
}, |
|
|
|
}); |
|
|
|
moveGroupNodes(groupId, { x: deltaX, y: deltaY }); |
|
|
|
dragStartRef.current = { x: e.clientX, y: e.clientY }; |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
const handleMouseUp = () => { |
|
|
|
setIsDragging(false); |
|
|
|
dragStartRef.current = null; |
|
|
|
}; |
|
|
|
|
|
|
|
window.addEventListener("mousemove", handleMouseMove); |
|
|
|
window.addEventListener("mouseup", handleMouseUp); |
|
|
|
|
|
|
|
return () => { |
|
|
|
window.removeEventListener("mousemove", handleMouseMove); |
|
|
|
window.removeEventListener("mouseup", handleMouseUp); |
|
|
|
}; |
|
|
|
}, [isDragging, group, groupId, moveGroupNodes, updateGroup, zoom]); |
|
|
|
|
|
|
|
if (!group) return null; |
|
|
|
|
|
|
|
@ -39,7 +89,8 @@ function GroupBackground({ groupId }: GroupBackgroundProps) { |
|
|
|
|
|
|
|
return ( |
|
|
|
<div |
|
|
|
className="absolute rounded-xl" |
|
|
|
className="absolute rounded-xl cursor-grab active:cursor-grabbing" |
|
|
|
onMouseDown={handleBackgroundMouseDown} |
|
|
|
style={{ |
|
|
|
left: group.position.x, |
|
|
|
top: group.position.y, |
|
|
|
@ -47,7 +98,7 @@ function GroupBackground({ groupId }: GroupBackgroundProps) { |
|
|
|
height: group.size.height, |
|
|
|
backgroundColor: `${bgColor}60`, |
|
|
|
border: group.isNbpInput ? `3px dashed rgba(255,255,255,0.25)` : `1px solid ${bgColor}`, |
|
|
|
pointerEvents: "none", |
|
|
|
pointerEvents: "auto", |
|
|
|
}} |
|
|
|
/> |
|
|
|
); |
|
|
|
@ -531,7 +582,7 @@ export function GroupBackgroundsPortal() { |
|
|
|
|
|
|
|
return ( |
|
|
|
<ViewportPortal> |
|
|
|
<div style={{ position: "absolute", top: 0, left: 0, zIndex: -1, pointerEvents: "none" }}> |
|
|
|
<div style={{ position: "absolute", top: 0, left: 0, zIndex: -1, pointerEvents: "auto" }}> |
|
|
|
{groupIds.map((groupId) => ( |
|
|
|
<GroupBackground key={groupId} groupId={groupId} /> |
|
|
|
))} |
|
|
|
|