From 2bc02547f6e70e159d818bce22447ab6dd991ce6 Mon Sep 17 00:00:00 2001 From: moklick Date: Sun, 17 Dec 2023 11:56:21 +0100 Subject: [PATCH] refactor(edges): optimize edge zIndex calculation --- .../src/components/EdgeWrapper/index.tsx | 48 +++++++++++++------ .../src/container/EdgeRenderer/index.tsx | 3 +- packages/react/src/hooks/useVisibleEdges.ts | 8 ++-- packages/react/src/types/edges.ts | 1 + .../svelte/src/lib/store/visible-edges.ts | 16 ++----- packages/system/src/utils/edges/general.ts | 27 +++++------ 6 files changed, 58 insertions(+), 45 deletions(-) diff --git a/packages/react/src/components/EdgeWrapper/index.tsx b/packages/react/src/components/EdgeWrapper/index.tsx index de387ce5..0b20af69 100644 --- a/packages/react/src/components/EdgeWrapper/index.tsx +++ b/packages/react/src/components/EdgeWrapper/index.tsx @@ -8,13 +8,14 @@ import { type Connection, getEdgePosition, errorMessages, + getEdgeZIndex, } from '@xyflow/system'; import { useStoreApi, useStore } from '../../hooks/useStore'; import { ARIA_EDGE_DESC_KEY } from '../A11yDescriptions'; import { EdgeAnchor } from '../Edges/EdgeAnchor'; import { getMouseHandler } from '../Edges/utils'; -import type { EdgeWrapperProps } from '../../types'; +import type { EdgeWrapperProps, Node } from '../../types'; import { builtinEdgeTypes } from './utils'; function EdgeWrapper({ @@ -56,7 +57,8 @@ function EdgeWrapper({ pathOptions, interactionWidth, edgeTypes, - zIndex, + zIndex: edgeZIndex, + elevateEdgesOnSelect, onError, }: EdgeWrapperProps): JSX.Element | null { let edgeType = type || 'default'; @@ -72,27 +74,45 @@ function EdgeWrapper({ const [updateHover, setUpdateHover] = useState(false); const [updating, setUpdating] = useState(false); const store = useStoreApi(); - const edgePosition = useStore( + const prevSourceNode = useRef(); + const prevTargetNode = useRef(); + const prevZIndex = useRef(edgeZIndex); + const prevEdgePosition = useRef | null>(null); + + const { edgePosition, zIndex } = useStore( useCallback( (state) => { const sourceNode = state.nodeLookup.get(source); const targetNode = state.nodeLookup.get(target); if (!sourceNode || !targetNode) { - return null; + return { edgePosition: null, zIndex: edgeZIndex }; } - return getEdgePosition({ - id, - sourceNode, - targetNode, - sourceHandle: sourceHandleId || null, - targetHandle: targetHandleId || null, - connectionMode: state.connectionMode, - onError: state.onError, - }); + const nodesChanged = prevSourceNode.current !== sourceNode || prevTargetNode.current !== targetNode; + + prevSourceNode.current = sourceNode; + prevTargetNode.current = targetNode; + + prevEdgePosition.current = nodesChanged + ? getEdgePosition({ + id, + sourceNode, + targetNode, + sourceHandle: sourceHandleId || null, + targetHandle: targetHandleId || null, + connectionMode: state.connectionMode, + onError: state.onError, + }) + : prevEdgePosition.current; + prevZIndex.current = getEdgeZIndex(selected, edgeZIndex, sourceNode, targetNode, elevateEdgesOnSelect); + + return { + edgePosition: prevEdgePosition.current, + zIndex: prevZIndex.current, + }; }, - [source, target] + [source, target, selected, edgeZIndex] ), shallow ); diff --git a/packages/react/src/container/EdgeRenderer/index.tsx b/packages/react/src/container/EdgeRenderer/index.tsx index a5e591f1..e1ae6a20 100644 --- a/packages/react/src/container/EdgeRenderer/index.tsx +++ b/packages/react/src/container/EdgeRenderer/index.tsx @@ -63,7 +63,7 @@ const EdgeRenderer = ({ children, }: EdgeRendererProps) => { const { edgesFocusable, edgesUpdatable, elementsSelectable, onError } = useStore(selector, shallow); - const edges = useVisibleEdges(onlyRenderVisibleElements, elevateEdgesOnSelect); + const edges = useVisibleEdges(onlyRenderVisibleElements); return (
@@ -121,6 +121,7 @@ const EdgeRenderer = ({ interactionWidth={edge.interactionWidth} onError={onError} edgeTypes={edgeTypes} + elevateEdgesOnSelect={elevateEdgesOnSelect} /> ); })} diff --git a/packages/react/src/hooks/useVisibleEdges.ts b/packages/react/src/hooks/useVisibleEdges.ts index ca035fbb..f7ebe606 100644 --- a/packages/react/src/hooks/useVisibleEdges.ts +++ b/packages/react/src/hooks/useVisibleEdges.ts @@ -1,11 +1,11 @@ import { useCallback } from 'react'; import { shallow } from 'zustand/shallow'; -import { adjustEdgeZIndex, isEdgeVisible } from '@xyflow/system'; +import { isEdgeVisible } from '@xyflow/system'; import { useStore } from '../hooks/useStore'; import { type Edge, type ReactFlowState } from '../types'; -function useVisibleEdges(onlyRenderVisible: boolean, elevateEdgesOnSelect: boolean): Edge[] { +function useVisibleEdges(onlyRenderVisible: boolean): Edge[] { const edges = useStore( useCallback( (s: ReactFlowState) => { @@ -29,9 +29,9 @@ function useVisibleEdges(onlyRenderVisible: boolean, elevateEdgesOnSelect: boole }) : s.edges; - return visibleEdges.map((edge) => adjustEdgeZIndex(edge, s.nodeLookup, elevateEdgesOnSelect)); + return visibleEdges; }, - [onlyRenderVisible, elevateEdgesOnSelect] + [onlyRenderVisible] ), shallow ); diff --git a/packages/react/src/types/edges.ts b/packages/react/src/types/edges.ts index 5af61d1e..2c0d3742 100644 --- a/packages/react/src/types/edges.ts +++ b/packages/react/src/types/edges.ts @@ -78,6 +78,7 @@ export type EdgeWrapperProps = Omit, 'sourceHandle' | 'targetHa pathOptions?: BezierPathOptions | SmoothStepPathOptions; edgeTypes?: EdgeTypes; onError?: OnError; + elevateEdgesOnSelect?: boolean; }; export type DefaultEdgeOptions = DefaultEdgeOptionsBase; diff --git a/packages/svelte/src/lib/store/visible-edges.ts b/packages/svelte/src/lib/store/visible-edges.ts index d9903261..1dd4a956 100644 --- a/packages/svelte/src/lib/store/visible-edges.ts +++ b/packages/svelte/src/lib/store/visible-edges.ts @@ -1,5 +1,5 @@ import { derived } from 'svelte/store'; -import { isEdgeVisible, getEdgePosition, adjustEdgeZIndex } from '@xyflow/system'; +import { isEdgeVisible, getEdgePosition, getEdgeZIndex } from '@xyflow/system'; import type { EdgeLayouted } from '$lib/types'; import type { SvelteFlowStoreState } from './types'; @@ -41,15 +41,8 @@ export function getVisibleEdges(store: SvelteFlowStoreState) { ); return derived( - [ - visibleEdges, - store.nodes, - store.nodeLookup, - store.connectionMode, - store.onlyRenderVisibleElements, - store.onerror - ], - ([visibleEdges, , nodeLookup, connectionMode, onlyRenderVisibleElements, onerror]) => { + [visibleEdges, store.nodes, store.nodeLookup, store.connectionMode, store.onerror], + ([visibleEdges, , nodeLookup, connectionMode, onerror]) => { const layoutedEdges = visibleEdges.reduce((res, edge) => { const sourceNode = nodeLookup.get(edge.source); const targetNode = nodeLookup.get(edge.target); @@ -70,7 +63,8 @@ export function getVisibleEdges(store: SvelteFlowStoreState) { if (edgePosition) { res.push({ - ...adjustEdgeZIndex(edge, nodeLookup, onlyRenderVisibleElements), + ...edge, + zIndex: getEdgeZIndex(edge.selected, edge.zIndex, sourceNode, targetNode, false), ...edgePosition }); } diff --git a/packages/system/src/utils/edges/general.ts b/packages/system/src/utils/edges/general.ts index fcde5f8b..73cbd7f9 100644 --- a/packages/system/src/utils/edges/general.ts +++ b/packages/system/src/utils/edges/general.ts @@ -23,26 +23,23 @@ export function getEdgeCenter({ return [centerX, centerY, xOffset, yOffset]; } -export function adjustEdgeZIndex(edge: EdgeBase, nodeLookup: Map, elevateEdgesOnSelect: boolean) { - const hasZIndex = isNumeric(edge.zIndex); - let z = hasZIndex ? edge.zIndex! : 0; +export function getEdgeZIndex( + selected: boolean | undefined, + zIndex: number | undefined, + sourceNode: NodeBase, + targetNode: NodeBase, + elevateEdgesOnSelect?: boolean +) { + const hasZIndex = isNumeric(zIndex); + let nextZIndex = hasZIndex ? zIndex! : 0; if (elevateEdgesOnSelect) { - const targetNode = nodeLookup.get(edge.target)!; - const sourceNode = nodeLookup.get(edge.source)!; - const edgeOrConnectedNodeSelected = edge.selected || targetNode?.selected || sourceNode?.selected; + const edgeOrConnectedNodeSelected = selected || targetNode?.selected || sourceNode?.selected; const selectedZIndex = Math.max(sourceNode?.[internalsSymbol]?.z || 0, targetNode?.[internalsSymbol]?.z || 0, 1000); - z = (hasZIndex ? edge.zIndex! : 0) + (edgeOrConnectedNodeSelected ? selectedZIndex : 0); - - if (edge.zIndex !== z) { - return { - ...edge, - zIndex: z, - }; - } + nextZIndex = (hasZIndex ? zIndex! : 0) + (edgeOrConnectedNodeSelected ? selectedZIndex : 0); } - return edge; + return nextZIndex; } type IsEdgeVisibleParams = {