From 04ff28dddc573d08c591478257a03048f72038b9 Mon Sep 17 00:00:00 2001 From: peterkogo Date: Tue, 24 Jun 2025 10:59:06 +0200 Subject: [PATCH 1/5] Elevate edges regardless of selected nodes --- packages/system/src/utils/edges/general.ts | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/packages/system/src/utils/edges/general.ts b/packages/system/src/utils/edges/general.ts index c094ab0b..db56b333 100644 --- a/packages/system/src/utils/edges/general.ts +++ b/packages/system/src/utils/edges/general.ts @@ -37,14 +37,9 @@ export function getElevatedEdgeZIndex({ zIndex = 0, elevateOnSelect = false, }: GetEdgeZIndexParams): number { - if (!elevateOnSelect) { - return zIndex; - } + const elevatedZ = Math.max(sourceNode.internals.z, targetNode.internals.z, elevateOnSelect && selected ? 1000 : 0); - const edgeOrConnectedNodeSelected = selected || targetNode.selected || sourceNode.selected; - const selectedZIndex = Math.max(sourceNode.internals.z || 0, targetNode.internals.z || 0, 1000); - - return zIndex + (edgeOrConnectedNodeSelected ? selectedZIndex : 0); + return zIndex + elevatedZ; } type IsEdgeVisibleParams = { From cf5597b8451e127db2ea3d2149634492c03eb57d Mon Sep 17 00:00:00 2001 From: peterkogo Date: Tue, 24 Jun 2025 10:59:23 +0200 Subject: [PATCH 2/5] Elevate child nodes +1 over the parent --- packages/system/src/utils/store.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/system/src/utils/store.ts b/packages/system/src/utils/store.ts index d94a7dff..1bfd029d 100644 --- a/packages/system/src/utils/store.ts +++ b/packages/system/src/utils/store.ts @@ -232,7 +232,7 @@ function calculateChildXYZ( return { x: absolutePosition.x, y: absolutePosition.y, - z: parentZ > childZ ? parentZ : childZ, + z: parentZ >= childZ ? parentZ + 1 : childZ, }; } From 323f07dd010a41ac5f0a70bf04490c6f9994d3ed Mon Sep 17 00:00:00 2001 From: peterkogo Date: Wed, 25 Jun 2025 15:43:01 +0200 Subject: [PATCH 3/5] revert some default behaviour from getElevatedZINdex --- packages/system/src/utils/edges/general.ts | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/packages/system/src/utils/edges/general.ts b/packages/system/src/utils/edges/general.ts index db56b333..8562494b 100644 --- a/packages/system/src/utils/edges/general.ts +++ b/packages/system/src/utils/edges/general.ts @@ -34,12 +34,21 @@ export function getElevatedEdgeZIndex({ sourceNode, targetNode, selected = false, - zIndex = 0, + zIndex, elevateOnSelect = false, }: GetEdgeZIndexParams): number { - const elevatedZ = Math.max(sourceNode.internals.z, targetNode.internals.z, elevateOnSelect && selected ? 1000 : 0); + if (zIndex !== undefined) { + return zIndex; + } - return zIndex + elevatedZ; + const edgeZ = elevateOnSelect && selected ? 1000 : 0; + + const nodeZ = Math.max( + sourceNode.parentId ? sourceNode.internals.z : 0, + targetNode.parentId ? targetNode.internals.z : 0 + ); + + return edgeZ + nodeZ; } type IsEdgeVisibleParams = { From a7abcc051713063943b13d6fa4e73cab80d5e6fc Mon Sep 17 00:00:00 2001 From: moklick Date: Thu, 26 Jun 2025 12:12:13 +0200 Subject: [PATCH 4/5] test(edges): zindex in sub flows --- examples/react/src/examples/Subflow/index.tsx | 4 +-- .../react/src/generic-tests/edges/general.ts | 33 +++++++++++++++++++ .../svelte/src/generic-tests/edges/general.ts | 33 +++++++++++++++++++ packages/system/src/utils/edges/general.ts | 5 +++ tests/playwright/e2e/edges.spec.ts | 21 ++++++++++++ 5 files changed, 94 insertions(+), 2 deletions(-) diff --git a/examples/react/src/examples/Subflow/index.tsx b/examples/react/src/examples/Subflow/index.tsx index 0f59a97e..82d0d413 100644 --- a/examples/react/src/examples/Subflow/index.tsx +++ b/examples/react/src/examples/Subflow/index.tsx @@ -152,10 +152,10 @@ const initialEdges: Edge[] = [ }, }, { id: 'e1-3', source: '1', target: '3' }, - { id: 'e3-4', source: '3', target: '4', zIndex: 100 }, + { id: 'e3-4', source: '3', target: '4' }, { id: 'e3-4b', source: '3', target: '4b' }, { id: 'e4a-4b1', source: '4a', target: '4b1' }, - { id: 'e4a-4b2', source: '4a', target: '4b2', zIndex: 100 }, + { id: 'e4a-4b2', source: '4a', target: '4b2' }, { id: 'e4b1-4b2', source: '4b1', target: '4b2' }, ]; diff --git a/examples/react/src/generic-tests/edges/general.ts b/examples/react/src/generic-tests/edges/general.ts index d00c57cc..dbdaec24 100644 --- a/examples/react/src/generic-tests/edges/general.ts +++ b/examples/react/src/generic-tests/edges/general.ts @@ -62,6 +62,29 @@ export default { data: { label: '11' }, position: { x: 100, y: 500 }, }, + { + id: '12', + data: { label: '12' }, + position: { x: 100, y: 600 }, + width: 200, + height: 100, + }, + { + id: '12-a', + parentId: '12', + data: { label: '12-a' }, + position: { x: 10, y: 20 }, + width: 50, + height: 50, + }, + { + id: '12-b', + parentId: '12', + data: { label: '12-b' }, + position: { x: 140, y: 20 }, + width: 50, + height: 50, + }, // { // id: '12', // data: { label: '12' }, @@ -145,6 +168,16 @@ export default { markerEnd: { type: MarkerType.Arrow }, markerStart: { type: MarkerType.ArrowClosed }, }, + { + id: 'subflow-edge', + source: '11', + target: '12-a', + }, + { + id: 'subflow-edge-2', + source: '12-a', + target: '12-b', + }, // { // id: 'updatable', // source: '9', diff --git a/examples/svelte/src/generic-tests/edges/general.ts b/examples/svelte/src/generic-tests/edges/general.ts index e17da374..cebbccd8 100644 --- a/examples/svelte/src/generic-tests/edges/general.ts +++ b/examples/svelte/src/generic-tests/edges/general.ts @@ -61,6 +61,29 @@ export default { id: '11', data: { label: '11' }, position: { x: 100, y: 500 } + }, + { + id: '12', + data: { label: '12' }, + position: { x: 100, y: 600 }, + width: 200, + height: 100 + }, + { + id: '12-a', + parentId: '12', + data: { label: '12-a' }, + position: { x: 10, y: 20 }, + width: 50, + height: 50 + }, + { + id: '12-b', + parentId: '12', + data: { label: '12-b' }, + position: { x: 140, y: 20 }, + width: 50, + height: 50 } // { // id: '12', @@ -142,6 +165,16 @@ export default { label: 'markers', markerEnd: { type: MarkerType.Arrow }, markerStart: { type: MarkerType.ArrowClosed } + }, + { + id: 'subflow-edge', + source: '11', + target: '12-a' + }, + { + id: 'subflow-edge-2', + source: '12-a', + target: '12-b' } // { // id: 'updatable', diff --git a/packages/system/src/utils/edges/general.ts b/packages/system/src/utils/edges/general.ts index 8562494b..c88c7272 100644 --- a/packages/system/src/utils/edges/general.ts +++ b/packages/system/src/utils/edges/general.ts @@ -30,6 +30,11 @@ export type GetEdgeZIndexParams = { elevateOnSelect?: boolean; }; +/** + * Returns the z-index for an edge based on the node it connects and whether it is selected. + * By default, edges are rendered below nodes. This behaviour is different for edges that are + * connected to nodes with a parent, as they are rendered above the parent node. + */ export function getElevatedEdgeZIndex({ sourceNode, targetNode, diff --git a/tests/playwright/e2e/edges.spec.ts b/tests/playwright/e2e/edges.spec.ts index 768f056c..6e26c8ca 100644 --- a/tests/playwright/e2e/edges.spec.ts +++ b/tests/playwright/e2e/edges.spec.ts @@ -153,5 +153,26 @@ test.describe('Edges', () => { await expect(edge).toHaveAttribute('marker-start', "url('#1__type=arrowclosed')"); await expect(edge).toHaveAttribute('marker-end', "url('#1__type=arrow')"); }); + + test('z-index', async ({ page }) => { + const svg = page.locator('svg', { has: page.locator('[data-id="edge-with-class"]') }); + + await expect(svg).toBeAttached(); + await expect(svg).toHaveCSS('z-index', '0'); + }); + + test('sub flow: normal node to child node, z-index', async ({ page }) => { + const svg = page.locator('svg', { has: page.locator('[data-id="subflow-edge"]') }); + + await expect(svg).toBeAttached(); + await expect(svg).toHaveCSS('z-index', '1'); + }); + + test('sub flow: child node to child node, z-index', async ({ page }) => { + const svg = page.locator('svg', { has: page.locator('[data-id="subflow-edge-2"]') }); + + await expect(svg).toBeAttached(); + await expect(svg).toHaveCSS('z-index', '1'); + }); }); }); From 90e9247adbdfa9d06db97e1d0d895e35c960551c Mon Sep 17 00:00:00 2001 From: moklick Date: Thu, 26 Jun 2025 12:19:43 +0200 Subject: [PATCH 5/5] chore(changeset): add --- .changeset/real-ties-travel.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/real-ties-travel.md diff --git a/.changeset/real-ties-travel.md b/.changeset/real-ties-travel.md new file mode 100644 index 00000000..14e29173 --- /dev/null +++ b/.changeset/real-ties-travel.md @@ -0,0 +1,5 @@ +--- +'@xyflow/system': patch +--- + +Render edges above nodes when they are within a subflow