More forgiving connections in vue (#6565)

The previous link connection code uses
[closest](https://developer.mozilla.org/en-US/docs/Web/API/Element/closest)
to find a slot. Closest only checks parents, not siblings. Since the
sought element has no children, this meant connection to a slot required
the mouse be directly over the slot.

This is changed by finding the closest (parent) widget or slot, and then
querying for the slot. For simplicity, this means introducing an
`lg-node-widget` class. As a result, connections can be made by hovering
anywhere over a valid widget.


![vue-connections_00001](https://github.com/user-attachments/assets/e556ff3f-8cbb-4198-998d-9c2aadf2c73c)


Resolves #6488

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6565-More-forgiving-connections-in-vue-2a16d73d365081e1bf46f5d54ec382d6)
by [Unito](https://www.unito.io)
This commit is contained in:
AustinMroz
2025-11-04 13:45:14 -08:00
committed by GitHub
parent a4fc68a9eb
commit 7c2a768d83
3 changed files with 13 additions and 14 deletions

View File

@@ -20,7 +20,9 @@ export const resolveSlotTargetCandidate = (
const { state: dragState, setCompatibleForKey } = useSlotLinkDragUIState() const { state: dragState, setCompatibleForKey } = useSlotLinkDragUIState()
if (!(target instanceof HTMLElement)) return null if (!(target instanceof HTMLElement)) return null
const elWithKey = target.closest<HTMLElement>('[data-slot-key]') const elWithKey = target
.closest('.lg-slot, .lg-node-widget')
?.querySelector<HTMLElement>('[data-slot-key]')
const key = elWithKey?.dataset['slotKey'] const key = elWithKey?.dataset['slotKey']
if (!key) return null if (!key) return null

View File

@@ -19,7 +19,7 @@
<div <div
v-for="(widget, index) in processedWidgets" v-for="(widget, index) in processedWidgets"
:key="`widget-${index}-${widget.name}`" :key="`widget-${index}-${widget.name}`"
class="group flex items-stretch has-[.widget-expands]:flex-1" class="lg-node-widget group flex items-stretch has-[.widget-expands]:flex-1"
> >
<!-- Widget Input Slot Dot --> <!-- Widget Input Slot Dot -->

View File

@@ -301,14 +301,12 @@ export function useSlotLinkInteraction({
hoveredSlotKey = dragContext.lastPointerTargetSlotKey hoveredSlotKey = dragContext.lastPointerTargetSlotKey
hoveredNodeId = dragContext.lastPointerTargetNodeId hoveredNodeId = dragContext.lastPointerTargetNodeId
} else if (target instanceof HTMLElement) { } else if (target instanceof HTMLElement) {
const elWithSlot = target.closest<HTMLElement>('[data-slot-key]') const elWithSlot = target
const elWithNode = elWithSlot .closest('.lg-slot, .lg-node-widget')
? null ?.querySelector<HTMLElement>('[data-slot-key]')
: target.closest<HTMLElement>('[data-node-id]') const elWithNode = target.closest<HTMLElement>('[data-node-id]')
hoveredSlotKey = elWithSlot?.dataset['slotKey'] ?? null hoveredSlotKey = elWithSlot?.dataset['slotKey'] ?? null
hoveredNodeId = hoveredSlotKey hoveredNodeId = elWithNode?.dataset['nodeId'] ?? null
? null
: (elWithNode?.dataset['nodeId'] ?? null)
dragContext.lastPointerEventTarget = target dragContext.lastPointerEventTarget = target
dragContext.lastPointerTargetSlotKey = hoveredSlotKey dragContext.lastPointerTargetSlotKey = hoveredSlotKey
dragContext.lastPointerTargetNodeId = hoveredNodeId dragContext.lastPointerTargetNodeId = hoveredNodeId
@@ -325,10 +323,8 @@ export function useSlotLinkInteraction({
const graph = app.canvas?.graph ?? null const graph = app.canvas?.graph ?? null
const context = { adapter, graph, session: dragContext } const context = { adapter, graph, session: dragContext }
const slotCandidate = resolveSlotTargetCandidate(target, context) const slotCandidate = resolveSlotTargetCandidate(target, context)
const nodeCandidate = slotCandidate const nodeCandidate = resolveNodeSurfaceSlotCandidate(target, context)
? null candidate = slotCandidate?.compatible ? slotCandidate : nodeCandidate
: resolveNodeSurfaceSlotCandidate(target, context)
candidate = slotCandidate ?? nodeCandidate
dragContext.lastHoverSlotKey = hoveredSlotKey dragContext.lastHoverSlotKey = hoveredSlotKey
dragContext.lastHoverNodeId = hoveredNodeId dragContext.lastHoverNodeId = hoveredNodeId
@@ -339,7 +335,8 @@ export function useSlotLinkInteraction({
slotCandidate.layout.type === 'input' slotCandidate.layout.type === 'input'
) )
setCompatibleForKey(key, !!slotCandidate.compatible) setCompatibleForKey(key, !!slotCandidate.compatible)
} else if (nodeCandidate) { }
if (nodeCandidate && !slotCandidate?.compatible) {
const key = getSlotKey( const key = getSlotKey(
nodeCandidate.layout.nodeId, nodeCandidate.layout.nodeId,
nodeCandidate.layout.index, nodeCandidate.layout.index,