Skip to content

Commit 7ca132d

Browse files
committed
fix(workflow): restyle loop drop target outline
1 parent 418aab0 commit 7ca132d

4 files changed

Lines changed: 29 additions & 18 deletions

File tree

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/utils/workflow-canvas-helpers.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import { TriggerUtils } from '@/lib/workflows/triggers/triggers'
44
import { clampPositionToContainer } from '@/app/workspace/[workspaceId]/w/[workflowId]/utils/node-position-utils'
55
import type { BlockState } from '@/stores/workflows/workflow/types'
66

7+
export const SUBFLOW_DROP_TARGET_CLASS = 'subflow-node-drop-target'
8+
79
/**
810
* Collects all descendant block IDs for container blocks (loop/parallel) in the given set.
911
* Used to treat a nested subflow as one unit when computing boundary edges (e.g. remove-from-subflow).
@@ -124,8 +126,8 @@ export function shouldHighlightContainerDropTarget(
124126
* Used when drag operations end or are cancelled.
125127
*/
126128
export function clearDragHighlights(): void {
127-
document.querySelectorAll('.loop-node-drag-over, .parallel-node-drag-over').forEach((el) => {
128-
el.classList.remove('loop-node-drag-over', 'parallel-node-drag-over')
129+
document.querySelectorAll(`.${SUBFLOW_DROP_TARGET_CLASS}`).forEach((el) => {
130+
el.classList.remove(SUBFLOW_DROP_TARGET_CLASS)
129131
})
130132
document.body.style.cursor = ''
131133
}

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/workflow.tsx

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ import {
8181
isInEditableElement,
8282
isPositionalTriggerBlock,
8383
resolveSelectionConflicts,
84+
SUBFLOW_DROP_TARGET_CLASS,
8485
shouldHighlightContainerDropTarget,
8586
validateTriggerPaste,
8687
} from '@/app/workspace/[workspaceId]/w/[workflowId]/utils'
@@ -503,19 +504,14 @@ const WorkflowContent = React.memo(
503504
)
504505

505506
/** Applies highlight styling to a container node during drag operations. */
506-
const highlightContainerNode = useCallback(
507-
(containerId: string, containerKind: 'loop' | 'parallel') => {
508-
clearDragHighlights()
509-
const containerElement = document.querySelector(`[data-id="${containerId}"]`)
510-
if (containerElement) {
511-
containerElement.classList.add(
512-
containerKind === 'loop' ? 'loop-node-drag-over' : 'parallel-node-drag-over'
513-
)
514-
document.body.style.cursor = 'copy'
515-
}
516-
},
517-
[]
518-
)
507+
const highlightContainerNode = useCallback((containerId: string) => {
508+
clearDragHighlights()
509+
const containerElement = document.querySelector(`[data-id="${containerId}"]`)
510+
if (containerElement) {
511+
containerElement.classList.add(SUBFLOW_DROP_TARGET_CLASS)
512+
document.body.style.cursor = 'copy'
513+
}
514+
}, [])
519515

520516
const { handleAutoLayout: autoLayoutWithFitView } = useAutoLayout(activeWorkflowId || null, {
521517
embedded,
@@ -2388,7 +2384,7 @@ const WorkflowContent = React.memo(
23882384
if (containerNode?.type === 'subflowNode') {
23892385
const kind = (containerNode.data as SubflowNodeData)?.kind
23902386
if (kind === 'loop' || kind === 'parallel') {
2391-
highlightContainerNode(containerInfo.loopId, kind)
2387+
highlightContainerNode(containerInfo.loopId)
23922388
}
23932389
}
23942390
} else {
@@ -3516,7 +3512,7 @@ const WorkflowContent = React.memo(
35163512
} else {
35173513
const kind = (bestContainerMatch.container.data as SubflowNodeData)?.kind
35183514
if (kind === 'loop' || kind === 'parallel') {
3519-
highlightContainerNode(bestContainerMatch.container.id, kind)
3515+
highlightContainerNode(bestContainerMatch.container.id)
35203516
}
35213517
}
35223518
} else {
@@ -3987,7 +3983,7 @@ const WorkflowContent = React.memo(
39873983
if (shouldHighlightContainer) {
39883984
const kind = (bestMatch.container.data as SubflowNodeData)?.kind
39893985
if (kind === 'loop' || kind === 'parallel') {
3990-
highlightContainerNode(bestMatch.container.id, kind)
3986+
highlightContainerNode(bestMatch.container.id)
39913987
}
39923988
}
39933989
}

apps/sim/lib/workflows/blocks/workflow-block-border-mount.test.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,13 @@ describe('WorkflowBlockBorder mount', () => {
323323
expect(host.querySelector('[data-handleid="loop-end-source"]')).toHaveStyle({ top: '69px' })
324324
expect(host.querySelector('svg')).toHaveAttribute('viewBox', '-36 -36 572 372')
325325
expect(host.querySelector('svg rect[fill="var(--surface-3)"]')).toBeTruthy()
326+
const dropTargetOutline = host.querySelector('[data-subflow-drop-target-outline]')
327+
expect(dropTargetOutline).toHaveClass(
328+
'rounded-2xl',
329+
'ring-[1.5px]',
330+
'ring-[var(--text-secondary)]',
331+
'[.subflow-node-drop-target_&]:opacity-100'
332+
)
326333
})
327334

328335
it('retracts a selected loop action swell after hover ends', () => {

packages/workflow-renderer/src/subflow/subflow-node-view.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -409,6 +409,12 @@ export function SubflowNodeView({
409409
data-nesting-level={nestingLevel}
410410
data-subflow-selected={isNodeSelected}
411411
>
412+
<div
413+
aria-hidden='true'
414+
className='pointer-events-none absolute inset-0 z-40 rounded-2xl opacity-0 ring-[1.5px] ring-[var(--text-secondary)] transition-opacity duration-100 [.subflow-node-drop-target_&]:opacity-100'
415+
data-subflow-drop-target-outline=''
416+
/>
417+
412418
<WorkflowBlockBorder
413419
nodeId={id}
414420
getConnectionNodeId={getConnectionNodeId}

0 commit comments

Comments
 (0)