refactor(edges): optimize edge zIndex calculation
This commit is contained in:
@@ -8,13 +8,14 @@ import {
|
|||||||
type Connection,
|
type Connection,
|
||||||
getEdgePosition,
|
getEdgePosition,
|
||||||
errorMessages,
|
errorMessages,
|
||||||
|
getEdgeZIndex,
|
||||||
} from '@xyflow/system';
|
} from '@xyflow/system';
|
||||||
|
|
||||||
import { useStoreApi, useStore } from '../../hooks/useStore';
|
import { useStoreApi, useStore } from '../../hooks/useStore';
|
||||||
import { ARIA_EDGE_DESC_KEY } from '../A11yDescriptions';
|
import { ARIA_EDGE_DESC_KEY } from '../A11yDescriptions';
|
||||||
import { EdgeAnchor } from '../Edges/EdgeAnchor';
|
import { EdgeAnchor } from '../Edges/EdgeAnchor';
|
||||||
import { getMouseHandler } from '../Edges/utils';
|
import { getMouseHandler } from '../Edges/utils';
|
||||||
import type { EdgeWrapperProps } from '../../types';
|
import type { EdgeWrapperProps, Node } from '../../types';
|
||||||
import { builtinEdgeTypes } from './utils';
|
import { builtinEdgeTypes } from './utils';
|
||||||
|
|
||||||
function EdgeWrapper({
|
function EdgeWrapper({
|
||||||
@@ -56,7 +57,8 @@ function EdgeWrapper({
|
|||||||
pathOptions,
|
pathOptions,
|
||||||
interactionWidth,
|
interactionWidth,
|
||||||
edgeTypes,
|
edgeTypes,
|
||||||
zIndex,
|
zIndex: edgeZIndex,
|
||||||
|
elevateEdgesOnSelect,
|
||||||
onError,
|
onError,
|
||||||
}: EdgeWrapperProps): JSX.Element | null {
|
}: EdgeWrapperProps): JSX.Element | null {
|
||||||
let edgeType = type || 'default';
|
let edgeType = type || 'default';
|
||||||
@@ -72,27 +74,45 @@ function EdgeWrapper({
|
|||||||
const [updateHover, setUpdateHover] = useState<boolean>(false);
|
const [updateHover, setUpdateHover] = useState<boolean>(false);
|
||||||
const [updating, setUpdating] = useState<boolean>(false);
|
const [updating, setUpdating] = useState<boolean>(false);
|
||||||
const store = useStoreApi();
|
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(
|
useCallback(
|
||||||
(state) => {
|
(state) => {
|
||||||
const sourceNode = state.nodeLookup.get(source);
|
const sourceNode = state.nodeLookup.get(source);
|
||||||
const targetNode = state.nodeLookup.get(target);
|
const targetNode = state.nodeLookup.get(target);
|
||||||
|
|
||||||
if (!sourceNode || !targetNode) {
|
if (!sourceNode || !targetNode) {
|
||||||
return null;
|
return { edgePosition: null, zIndex: edgeZIndex };
|
||||||
}
|
}
|
||||||
|
|
||||||
return getEdgePosition({
|
const nodesChanged = prevSourceNode.current !== sourceNode || prevTargetNode.current !== targetNode;
|
||||||
id,
|
|
||||||
sourceNode,
|
prevSourceNode.current = sourceNode;
|
||||||
targetNode,
|
prevTargetNode.current = targetNode;
|
||||||
sourceHandle: sourceHandleId || null,
|
|
||||||
targetHandle: targetHandleId || null,
|
prevEdgePosition.current = nodesChanged
|
||||||
connectionMode: state.connectionMode,
|
? getEdgePosition({
|
||||||
onError: state.onError,
|
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
|
shallow
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ const EdgeRenderer = ({
|
|||||||
children,
|
children,
|
||||||
}: EdgeRendererProps) => {
|
}: EdgeRendererProps) => {
|
||||||
const { edgesFocusable, edgesUpdatable, elementsSelectable, onError } = useStore(selector, shallow);
|
const { edgesFocusable, edgesUpdatable, elementsSelectable, onError } = useStore(selector, shallow);
|
||||||
const edges = useVisibleEdges(onlyRenderVisibleElements, elevateEdgesOnSelect);
|
const edges = useVisibleEdges(onlyRenderVisibleElements);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="react-flow__edges">
|
<div className="react-flow__edges">
|
||||||
@@ -121,6 +121,7 @@ const EdgeRenderer = ({
|
|||||||
interactionWidth={edge.interactionWidth}
|
interactionWidth={edge.interactionWidth}
|
||||||
onError={onError}
|
onError={onError}
|
||||||
edgeTypes={edgeTypes}
|
edgeTypes={edgeTypes}
|
||||||
|
elevateEdgesOnSelect={elevateEdgesOnSelect}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
import { shallow } from 'zustand/shallow';
|
import { shallow } from 'zustand/shallow';
|
||||||
import { adjustEdgeZIndex, isEdgeVisible } from '@xyflow/system';
|
import { isEdgeVisible } from '@xyflow/system';
|
||||||
|
|
||||||
import { useStore } from '../hooks/useStore';
|
import { useStore } from '../hooks/useStore';
|
||||||
import { type Edge, type ReactFlowState } from '../types';
|
import { type Edge, type ReactFlowState } from '../types';
|
||||||
|
|
||||||
function useVisibleEdges(onlyRenderVisible: boolean, elevateEdgesOnSelect: boolean): Edge[] {
|
function useVisibleEdges(onlyRenderVisible: boolean): Edge[] {
|
||||||
const edges = useStore(
|
const edges = useStore(
|
||||||
useCallback(
|
useCallback(
|
||||||
(s: ReactFlowState) => {
|
(s: ReactFlowState) => {
|
||||||
@@ -29,9 +29,9 @@ function useVisibleEdges(onlyRenderVisible: boolean, elevateEdgesOnSelect: boole
|
|||||||
})
|
})
|
||||||
: s.edges;
|
: s.edges;
|
||||||
|
|
||||||
return visibleEdges.map((edge) => adjustEdgeZIndex(edge, s.nodeLookup, elevateEdgesOnSelect));
|
return visibleEdges;
|
||||||
},
|
},
|
||||||
[onlyRenderVisible, elevateEdgesOnSelect]
|
[onlyRenderVisible]
|
||||||
),
|
),
|
||||||
shallow
|
shallow
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -78,6 +78,7 @@ export type EdgeWrapperProps<T = any> = Omit<Edge<T>, 'sourceHandle' | 'targetHa
|
|||||||
pathOptions?: BezierPathOptions | SmoothStepPathOptions;
|
pathOptions?: BezierPathOptions | SmoothStepPathOptions;
|
||||||
edgeTypes?: EdgeTypes;
|
edgeTypes?: EdgeTypes;
|
||||||
onError?: OnError;
|
onError?: OnError;
|
||||||
|
elevateEdgesOnSelect?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type DefaultEdgeOptions = DefaultEdgeOptionsBase<Edge>;
|
export type DefaultEdgeOptions = DefaultEdgeOptionsBase<Edge>;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { derived } from 'svelte/store';
|
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 { EdgeLayouted } from '$lib/types';
|
||||||
import type { SvelteFlowStoreState } from './types';
|
import type { SvelteFlowStoreState } from './types';
|
||||||
@@ -41,15 +41,8 @@ export function getVisibleEdges(store: SvelteFlowStoreState) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return derived(
|
return derived(
|
||||||
[
|
[visibleEdges, store.nodes, store.nodeLookup, store.connectionMode, store.onerror],
|
||||||
visibleEdges,
|
([visibleEdges, , nodeLookup, connectionMode, onerror]) => {
|
||||||
store.nodes,
|
|
||||||
store.nodeLookup,
|
|
||||||
store.connectionMode,
|
|
||||||
store.onlyRenderVisibleElements,
|
|
||||||
store.onerror
|
|
||||||
],
|
|
||||||
([visibleEdges, , nodeLookup, connectionMode, onlyRenderVisibleElements, onerror]) => {
|
|
||||||
const layoutedEdges = visibleEdges.reduce<EdgeLayouted[]>((res, edge) => {
|
const layoutedEdges = visibleEdges.reduce<EdgeLayouted[]>((res, edge) => {
|
||||||
const sourceNode = nodeLookup.get(edge.source);
|
const sourceNode = nodeLookup.get(edge.source);
|
||||||
const targetNode = nodeLookup.get(edge.target);
|
const targetNode = nodeLookup.get(edge.target);
|
||||||
@@ -70,7 +63,8 @@ export function getVisibleEdges(store: SvelteFlowStoreState) {
|
|||||||
|
|
||||||
if (edgePosition) {
|
if (edgePosition) {
|
||||||
res.push({
|
res.push({
|
||||||
...adjustEdgeZIndex(edge, nodeLookup, onlyRenderVisibleElements),
|
...edge,
|
||||||
|
zIndex: getEdgeZIndex(edge.selected, edge.zIndex, sourceNode, targetNode, false),
|
||||||
...edgePosition
|
...edgePosition
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,26 +23,23 @@ export function getEdgeCenter({
|
|||||||
return [centerX, centerY, xOffset, yOffset];
|
return [centerX, centerY, xOffset, yOffset];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function adjustEdgeZIndex(edge: EdgeBase, nodeLookup: Map<string, NodeBase>, elevateEdgesOnSelect: boolean) {
|
export function getEdgeZIndex(
|
||||||
const hasZIndex = isNumeric(edge.zIndex);
|
selected: boolean | undefined,
|
||||||
let z = hasZIndex ? edge.zIndex! : 0;
|
zIndex: number | undefined,
|
||||||
|
sourceNode: NodeBase,
|
||||||
|
targetNode: NodeBase,
|
||||||
|
elevateEdgesOnSelect?: boolean
|
||||||
|
) {
|
||||||
|
const hasZIndex = isNumeric(zIndex);
|
||||||
|
let nextZIndex = hasZIndex ? zIndex! : 0;
|
||||||
|
|
||||||
if (elevateEdgesOnSelect) {
|
if (elevateEdgesOnSelect) {
|
||||||
const targetNode = nodeLookup.get(edge.target)!;
|
const edgeOrConnectedNodeSelected = selected || targetNode?.selected || sourceNode?.selected;
|
||||||
const sourceNode = nodeLookup.get(edge.source)!;
|
|
||||||
const edgeOrConnectedNodeSelected = edge.selected || targetNode?.selected || sourceNode?.selected;
|
|
||||||
const selectedZIndex = Math.max(sourceNode?.[internalsSymbol]?.z || 0, targetNode?.[internalsSymbol]?.z || 0, 1000);
|
const selectedZIndex = Math.max(sourceNode?.[internalsSymbol]?.z || 0, targetNode?.[internalsSymbol]?.z || 0, 1000);
|
||||||
z = (hasZIndex ? edge.zIndex! : 0) + (edgeOrConnectedNodeSelected ? selectedZIndex : 0);
|
nextZIndex = (hasZIndex ? zIndex! : 0) + (edgeOrConnectedNodeSelected ? selectedZIndex : 0);
|
||||||
|
|
||||||
if (edge.zIndex !== z) {
|
|
||||||
return {
|
|
||||||
...edge,
|
|
||||||
zIndex: z,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return edge;
|
return nextZIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
type IsEdgeVisibleParams = {
|
type IsEdgeVisibleParams = {
|
||||||
|
|||||||
Reference in New Issue
Block a user