refactor(edges): optimize edge zIndex calculation

This commit is contained in:
moklick
2023-12-17 11:56:21 +01:00
parent c3834208d2
commit 2bc02547f6
6 changed files with 58 additions and 45 deletions
@@ -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<boolean>(false);
const [updating, setUpdating] = useState<boolean>(false);
const store = useStoreApi();
const edgePosition = useStore(
const prevSourceNode = useRef<Node | undefined>();
const prevTargetNode = useRef<Node | undefined>();
const prevZIndex = useRef<number | undefined>(edgeZIndex);
const prevEdgePosition = useRef<ReturnType<typeof getEdgePosition> | 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
);
@@ -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 (
<div className="react-flow__edges">
@@ -121,6 +121,7 @@ const EdgeRenderer = ({
interactionWidth={edge.interactionWidth}
onError={onError}
edgeTypes={edgeTypes}
elevateEdgesOnSelect={elevateEdgesOnSelect}
/>
);
})}
+4 -4
View File
@@ -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
);
+1
View File
@@ -78,6 +78,7 @@ export type EdgeWrapperProps<T = any> = Omit<Edge<T>, 'sourceHandle' | 'targetHa
pathOptions?: BezierPathOptions | SmoothStepPathOptions;
edgeTypes?: EdgeTypes;
onError?: OnError;
elevateEdgesOnSelect?: boolean;
};
export type DefaultEdgeOptions = DefaultEdgeOptionsBase<Edge>;
+5 -11
View File
@@ -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<EdgeLayouted[]>((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
});
}
+12 -15
View File
@@ -23,26 +23,23 @@ export function getEdgeCenter({
return [centerX, centerY, xOffset, yOffset];
}
export function adjustEdgeZIndex(edge: EdgeBase, nodeLookup: Map<string, NodeBase>, 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 = {