refactor(edges): simplify edge rendering
This commit is contained in:
@@ -26,6 +26,7 @@ export default (EdgeComponent: ComponentType<EdgeProps>) => {
|
|||||||
labelBgPadding,
|
labelBgPadding,
|
||||||
labelBgBorderRadius,
|
labelBgBorderRadius,
|
||||||
style,
|
style,
|
||||||
|
zIndex,
|
||||||
source,
|
source,
|
||||||
target,
|
target,
|
||||||
isSelectable,
|
isSelectable,
|
||||||
@@ -201,86 +202,88 @@ export default (EdgeComponent: ComponentType<EdgeProps>) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<g
|
<svg className="react-flow__edgewrapper" style={{ zIndex }}>
|
||||||
className={cc([
|
<g
|
||||||
'react-flow__edge',
|
className={cc([
|
||||||
`react-flow__edge-${type}`,
|
'react-flow__edge',
|
||||||
className,
|
`react-flow__edge-${type}`,
|
||||||
{ selected, animated, inactive, updating: updateHover },
|
className,
|
||||||
])}
|
{ selected, animated, inactive, updating: updateHover },
|
||||||
onClick={onEdgeClick}
|
])}
|
||||||
onDoubleClick={onEdgeDoubleClickHandler}
|
onClick={onEdgeClick}
|
||||||
onContextMenu={onEdgeContextMenu}
|
onDoubleClick={onEdgeDoubleClickHandler}
|
||||||
onMouseEnter={onEdgeMouseEnter}
|
onContextMenu={onEdgeContextMenu}
|
||||||
onMouseMove={onEdgeMouseMove}
|
onMouseEnter={onEdgeMouseEnter}
|
||||||
onMouseLeave={onEdgeMouseLeave}
|
onMouseMove={onEdgeMouseMove}
|
||||||
onKeyDown={isFocusable ? onKeyDown : undefined}
|
onMouseLeave={onEdgeMouseLeave}
|
||||||
tabIndex={isFocusable ? 0 : undefined}
|
onKeyDown={isFocusable ? onKeyDown : undefined}
|
||||||
role={isFocusable ? 'button' : 'img'}
|
tabIndex={isFocusable ? 0 : undefined}
|
||||||
data-id={id}
|
role={isFocusable ? 'button' : 'img'}
|
||||||
data-testid={`rf__edge-${id}`}
|
data-id={id}
|
||||||
aria-label={ariaLabel === null ? undefined : ariaLabel ? ariaLabel : `Edge from ${source} to ${target}`}
|
data-testid={`rf__edge-${id}`}
|
||||||
aria-describedby={isFocusable ? `${ARIA_EDGE_DESC_KEY}-${rfId}` : undefined}
|
aria-label={ariaLabel === null ? undefined : ariaLabel ? ariaLabel : `Edge from ${source} to ${target}`}
|
||||||
ref={edgeRef}
|
aria-describedby={isFocusable ? `${ARIA_EDGE_DESC_KEY}-${rfId}` : undefined}
|
||||||
>
|
ref={edgeRef}
|
||||||
{!updating && (
|
>
|
||||||
<EdgeComponent
|
{!updating && (
|
||||||
id={id}
|
<EdgeComponent
|
||||||
source={source}
|
id={id}
|
||||||
target={target}
|
source={source}
|
||||||
selected={selected}
|
target={target}
|
||||||
animated={animated}
|
selected={selected}
|
||||||
label={label}
|
animated={animated}
|
||||||
labelStyle={labelStyle}
|
label={label}
|
||||||
labelShowBg={labelShowBg}
|
labelStyle={labelStyle}
|
||||||
labelBgStyle={labelBgStyle}
|
labelShowBg={labelShowBg}
|
||||||
labelBgPadding={labelBgPadding}
|
labelBgStyle={labelBgStyle}
|
||||||
labelBgBorderRadius={labelBgBorderRadius}
|
labelBgPadding={labelBgPadding}
|
||||||
data={data}
|
labelBgBorderRadius={labelBgBorderRadius}
|
||||||
style={style}
|
data={data}
|
||||||
sourceX={edgePosition.sourceX}
|
style={style}
|
||||||
sourceY={edgePosition.sourceY}
|
sourceX={edgePosition.sourceX}
|
||||||
targetX={edgePosition.targetX}
|
sourceY={edgePosition.sourceY}
|
||||||
targetY={edgePosition.targetY}
|
targetX={edgePosition.targetX}
|
||||||
sourcePosition={edgePosition.sourcePosition}
|
targetY={edgePosition.targetY}
|
||||||
targetPosition={edgePosition.targetPosition}
|
sourcePosition={edgePosition.sourcePosition}
|
||||||
sourceHandleId={sourceHandleId}
|
targetPosition={edgePosition.targetPosition}
|
||||||
targetHandleId={targetHandleId}
|
sourceHandleId={sourceHandleId}
|
||||||
markerStart={markerStartUrl}
|
targetHandleId={targetHandleId}
|
||||||
markerEnd={markerEndUrl}
|
markerStart={markerStartUrl}
|
||||||
pathOptions={pathOptions}
|
markerEnd={markerEndUrl}
|
||||||
interactionWidth={interactionWidth}
|
pathOptions={pathOptions}
|
||||||
/>
|
interactionWidth={interactionWidth}
|
||||||
)}
|
/>
|
||||||
{isUpdatable && (
|
)}
|
||||||
<>
|
{isUpdatable && (
|
||||||
{(isUpdatable === 'source' || isUpdatable === true) && (
|
<>
|
||||||
<EdgeAnchor
|
{(isUpdatable === 'source' || isUpdatable === true) && (
|
||||||
position={edgePosition.sourcePosition}
|
<EdgeAnchor
|
||||||
centerX={edgePosition.sourceX}
|
position={edgePosition.sourcePosition}
|
||||||
centerY={edgePosition.sourceY}
|
centerX={edgePosition.sourceX}
|
||||||
radius={edgeUpdaterRadius}
|
centerY={edgePosition.sourceY}
|
||||||
onMouseDown={onEdgeUpdaterSourceMouseDown}
|
radius={edgeUpdaterRadius}
|
||||||
onMouseEnter={onEdgeUpdaterMouseEnter}
|
onMouseDown={onEdgeUpdaterSourceMouseDown}
|
||||||
onMouseOut={onEdgeUpdaterMouseOut}
|
onMouseEnter={onEdgeUpdaterMouseEnter}
|
||||||
type="source"
|
onMouseOut={onEdgeUpdaterMouseOut}
|
||||||
/>
|
type="source"
|
||||||
)}
|
/>
|
||||||
{(isUpdatable === 'target' || isUpdatable === true) && (
|
)}
|
||||||
<EdgeAnchor
|
{(isUpdatable === 'target' || isUpdatable === true) && (
|
||||||
position={edgePosition.targetPosition}
|
<EdgeAnchor
|
||||||
centerX={edgePosition.targetX}
|
position={edgePosition.targetPosition}
|
||||||
centerY={edgePosition.targetY}
|
centerX={edgePosition.targetX}
|
||||||
radius={edgeUpdaterRadius}
|
centerY={edgePosition.targetY}
|
||||||
onMouseDown={onEdgeUpdaterTargetMouseDown}
|
radius={edgeUpdaterRadius}
|
||||||
onMouseEnter={onEdgeUpdaterMouseEnter}
|
onMouseDown={onEdgeUpdaterTargetMouseDown}
|
||||||
onMouseOut={onEdgeUpdaterMouseOut}
|
onMouseEnter={onEdgeUpdaterMouseEnter}
|
||||||
type="target"
|
onMouseOut={onEdgeUpdaterMouseOut}
|
||||||
/>
|
type="target"
|
||||||
)}
|
/>
|
||||||
</>
|
)}
|
||||||
)}
|
</>
|
||||||
</g>
|
)}
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -63,82 +63,76 @@ const EdgeRenderer = ({
|
|||||||
children,
|
children,
|
||||||
}: EdgeRendererProps) => {
|
}: EdgeRendererProps) => {
|
||||||
const { edgesFocusable, edgesUpdatable, elementsSelectable, onError } = useStore(selector, shallow);
|
const { edgesFocusable, edgesUpdatable, elementsSelectable, onError } = useStore(selector, shallow);
|
||||||
// we are grouping edges by zIndex here in order to be able to render them in the correct order
|
const edges = useVisibleEdges(onlyRenderVisibleElements, elevateEdgesOnSelect);
|
||||||
// each zIndex gets its own svg element
|
|
||||||
const edgeTree = useVisibleEdges(onlyRenderVisibleElements, elevateEdgesOnSelect);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<div className="react-flow__edges">
|
||||||
{edgeTree.map(({ level, edges, isMaxLevel }) => (
|
<svg className="react-flow__container">
|
||||||
<svg key={level} style={{ zIndex: level }} className="react-flow__edges react-flow__container">
|
<MarkerDefinitions defaultColor={defaultMarkerColor} rfId={rfId} />
|
||||||
{isMaxLevel && <MarkerDefinitions defaultColor={defaultMarkerColor} rfId={rfId} />}
|
</svg>
|
||||||
<>
|
|
||||||
{edges.map((edge) => {
|
|
||||||
let edgeType = edge.type || 'default';
|
|
||||||
|
|
||||||
if (!edgeTypes[edgeType]) {
|
{edges.map((edge) => {
|
||||||
onError?.('011', errorMessages['error011'](edgeType));
|
let edgeType = edge.type || 'default';
|
||||||
edgeType = 'default';
|
|
||||||
}
|
|
||||||
|
|
||||||
const EdgeComponent = edgeTypes[edgeType];
|
if (!edgeTypes[edgeType]) {
|
||||||
const isFocusable = !!(edge.focusable || (edgesFocusable && typeof edge.focusable === 'undefined'));
|
onError?.('011', errorMessages['error011'](edgeType));
|
||||||
const isUpdatable =
|
edgeType = 'default';
|
||||||
typeof onEdgeUpdate !== 'undefined' &&
|
}
|
||||||
(edge.updatable || (edgesUpdatable && typeof edge.updatable === 'undefined'));
|
|
||||||
const isSelectable = !!(
|
const EdgeComponent = edgeTypes[edgeType];
|
||||||
edge.selectable ||
|
const isFocusable = !!(edge.focusable || (edgesFocusable && typeof edge.focusable === 'undefined'));
|
||||||
(elementsSelectable && typeof edge.selectable === 'undefined')
|
const isUpdatable =
|
||||||
);
|
typeof onEdgeUpdate !== 'undefined' &&
|
||||||
|
(edge.updatable || (edgesUpdatable && typeof edge.updatable === 'undefined'));
|
||||||
|
const isSelectable = !!(edge.selectable || (elementsSelectable && typeof edge.selectable === 'undefined'));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<EdgeComponent
|
||||||
|
key={edge.id}
|
||||||
|
id={edge.id}
|
||||||
|
className={cc([edge.className, noPanClassName])}
|
||||||
|
type={edgeType}
|
||||||
|
data={edge.data}
|
||||||
|
selected={!!edge.selected}
|
||||||
|
animated={!!edge.animated}
|
||||||
|
hidden={!!edge.hidden}
|
||||||
|
label={edge.label}
|
||||||
|
labelStyle={edge.labelStyle}
|
||||||
|
labelShowBg={edge.labelShowBg}
|
||||||
|
labelBgStyle={edge.labelBgStyle}
|
||||||
|
labelBgPadding={edge.labelBgPadding}
|
||||||
|
labelBgBorderRadius={edge.labelBgBorderRadius}
|
||||||
|
style={edge.style}
|
||||||
|
zIndex={edge.zIndex}
|
||||||
|
source={edge.source}
|
||||||
|
target={edge.target}
|
||||||
|
sourceHandleId={edge.sourceHandle}
|
||||||
|
targetHandleId={edge.targetHandle}
|
||||||
|
markerEnd={edge.markerEnd}
|
||||||
|
markerStart={edge.markerStart}
|
||||||
|
isSelectable={isSelectable}
|
||||||
|
onEdgeUpdate={onEdgeUpdate}
|
||||||
|
onContextMenu={onEdgeContextMenu}
|
||||||
|
onMouseEnter={onEdgeMouseEnter}
|
||||||
|
onMouseMove={onEdgeMouseMove}
|
||||||
|
onMouseLeave={onEdgeMouseLeave}
|
||||||
|
onClick={onEdgeClick}
|
||||||
|
edgeUpdaterRadius={edgeUpdaterRadius}
|
||||||
|
onEdgeDoubleClick={onEdgeDoubleClick}
|
||||||
|
onEdgeUpdateStart={onEdgeUpdateStart}
|
||||||
|
onEdgeUpdateEnd={onEdgeUpdateEnd}
|
||||||
|
rfId={rfId}
|
||||||
|
ariaLabel={edge.ariaLabel}
|
||||||
|
isFocusable={isFocusable}
|
||||||
|
isUpdatable={isUpdatable}
|
||||||
|
pathOptions={'pathOptions' in edge ? edge.pathOptions : undefined}
|
||||||
|
interactionWidth={edge.interactionWidth}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
|
||||||
return (
|
|
||||||
<EdgeComponent
|
|
||||||
key={edge.id}
|
|
||||||
id={edge.id}
|
|
||||||
className={cc([edge.className, noPanClassName])}
|
|
||||||
type={edge.type}
|
|
||||||
data={edge.data}
|
|
||||||
selected={!!edge.selected}
|
|
||||||
animated={!!edge.animated}
|
|
||||||
hidden={!!edge.hidden}
|
|
||||||
label={edge.label}
|
|
||||||
labelStyle={edge.labelStyle}
|
|
||||||
labelShowBg={edge.labelShowBg}
|
|
||||||
labelBgStyle={edge.labelBgStyle}
|
|
||||||
labelBgPadding={edge.labelBgPadding}
|
|
||||||
labelBgBorderRadius={edge.labelBgBorderRadius}
|
|
||||||
style={edge.style}
|
|
||||||
source={edge.source}
|
|
||||||
target={edge.target}
|
|
||||||
sourceHandleId={edge.sourceHandle}
|
|
||||||
targetHandleId={edge.targetHandle}
|
|
||||||
markerEnd={edge.markerEnd}
|
|
||||||
markerStart={edge.markerStart}
|
|
||||||
isSelectable={isSelectable}
|
|
||||||
onEdgeUpdate={onEdgeUpdate}
|
|
||||||
onContextMenu={onEdgeContextMenu}
|
|
||||||
onMouseEnter={onEdgeMouseEnter}
|
|
||||||
onMouseMove={onEdgeMouseMove}
|
|
||||||
onMouseLeave={onEdgeMouseLeave}
|
|
||||||
onClick={onEdgeClick}
|
|
||||||
edgeUpdaterRadius={edgeUpdaterRadius}
|
|
||||||
onEdgeDoubleClick={onEdgeDoubleClick}
|
|
||||||
onEdgeUpdateStart={onEdgeUpdateStart}
|
|
||||||
onEdgeUpdateEnd={onEdgeUpdateEnd}
|
|
||||||
rfId={rfId}
|
|
||||||
ariaLabel={edge.ariaLabel}
|
|
||||||
isFocusable={isFocusable}
|
|
||||||
isUpdatable={isUpdatable}
|
|
||||||
pathOptions={'pathOptions' in edge ? edge.pathOptions : undefined}
|
|
||||||
interactionWidth={edge.interactionWidth}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</>
|
|
||||||
</svg>
|
|
||||||
))}
|
|
||||||
{children}
|
{children}
|
||||||
</>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
import { GroupedEdges, groupEdgesByZLevel, isEdgeVisible } from '@xyflow/system';
|
import { shallow } from 'zustand/shallow';
|
||||||
|
import { adjustEdgeZIndex, isEdgeVisible } from '@xyflow/system';
|
||||||
|
|
||||||
import { useStore } from '../hooks/useStore';
|
import { useStore } from '../hooks/useStore';
|
||||||
import { Edge, type ReactFlowState } from '../types';
|
import { type Edge, type ReactFlowState } from '../types';
|
||||||
import { shallow } from 'zustand/shallow';
|
|
||||||
|
|
||||||
function useVisibleEdges(onlyRenderVisible: boolean, elevateEdgesOnSelect: boolean): GroupedEdges<Edge>[] {
|
function useVisibleEdges(onlyRenderVisible: boolean, elevateEdgesOnSelect: boolean): Edge[] {
|
||||||
const edges = useStore(
|
const edges = useStore(
|
||||||
useCallback(
|
useCallback(
|
||||||
(s: ReactFlowState) => {
|
(s: ReactFlowState) => {
|
||||||
@@ -29,20 +29,11 @@ function useVisibleEdges(onlyRenderVisible: boolean, elevateEdgesOnSelect: boole
|
|||||||
})
|
})
|
||||||
: s.edges;
|
: s.edges;
|
||||||
|
|
||||||
return groupEdgesByZLevel(visibleEdges, s.nodeLookup, elevateEdgesOnSelect);
|
return visibleEdges.map((edge) => adjustEdgeZIndex(edge, s.nodeLookup, elevateEdgesOnSelect));
|
||||||
},
|
},
|
||||||
[onlyRenderVisible, elevateEdgesOnSelect]
|
[onlyRenderVisible, elevateEdgesOnSelect]
|
||||||
),
|
),
|
||||||
(groupA, groupB) => {
|
shallow
|
||||||
const unEqual = groupA.some(
|
|
||||||
(item, index) =>
|
|
||||||
item.isMaxLevel !== groupB[index].isMaxLevel ||
|
|
||||||
item.level !== groupB[index].level ||
|
|
||||||
!shallow(item.edges, groupB[index].edges)
|
|
||||||
);
|
|
||||||
|
|
||||||
return !unEqual;
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return edges;
|
return edges;
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
export let target: $$Props['target'] = '';
|
export let target: $$Props['target'] = '';
|
||||||
export let data: $$Props['data'] = {};
|
export let data: $$Props['data'] = {};
|
||||||
export let style: $$Props['style'] = undefined;
|
export let style: $$Props['style'] = undefined;
|
||||||
|
export let zIndex: $$Props['zIndex'] = undefined;
|
||||||
|
|
||||||
export let animated: $$Props['animated'] = false;
|
export let animated: $$Props['animated'] = false;
|
||||||
export let selected: $$Props['selected'] = false;
|
export let selected: $$Props['selected'] = false;
|
||||||
@@ -95,42 +96,44 @@
|
|||||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
<!-- svelte-ignore a11y-no-noninteractive-element-interactions -->
|
<!-- svelte-ignore a11y-no-noninteractive-element-interactions -->
|
||||||
{#if !hidden}
|
{#if !hidden}
|
||||||
<g
|
<svg class="svelte-flow__edgewrapper" style:zIndex>
|
||||||
class={cc(['svelte-flow__edge', className])}
|
<g
|
||||||
class:animated
|
class={cc(['svelte-flow__edge', className])}
|
||||||
class:selected
|
class:animated
|
||||||
data-id={id}
|
class:selected
|
||||||
on:click={onClick}
|
data-id={id}
|
||||||
on:contextmenu={onContextMenu}
|
on:click={onClick}
|
||||||
aria-label={ariaLabel === null
|
on:contextmenu={onContextMenu}
|
||||||
? undefined
|
aria-label={ariaLabel === null
|
||||||
: ariaLabel
|
? undefined
|
||||||
? ariaLabel
|
: ariaLabel
|
||||||
: `Edge from ${source} to ${target}`}
|
? ariaLabel
|
||||||
role="img"
|
: `Edge from ${source} to ${target}`}
|
||||||
>
|
role="img"
|
||||||
<svelte:component
|
>
|
||||||
this={edgeComponent}
|
<svelte:component
|
||||||
{id}
|
this={edgeComponent}
|
||||||
{source}
|
{id}
|
||||||
{target}
|
{source}
|
||||||
{sourceX}
|
{target}
|
||||||
{sourceY}
|
{sourceX}
|
||||||
{targetX}
|
{sourceY}
|
||||||
{targetY}
|
{targetX}
|
||||||
{sourcePosition}
|
{targetY}
|
||||||
{targetPosition}
|
{sourcePosition}
|
||||||
{animated}
|
{targetPosition}
|
||||||
{selected}
|
{animated}
|
||||||
{label}
|
{selected}
|
||||||
{labelStyle}
|
{label}
|
||||||
{data}
|
{labelStyle}
|
||||||
{style}
|
{data}
|
||||||
{interactionWidth}
|
{style}
|
||||||
sourceHandleId={sourceHandle}
|
{interactionWidth}
|
||||||
targetHandleId={targetHandle}
|
sourceHandleId={sourceHandle}
|
||||||
markerStart={markerStartUrl}
|
targetHandleId={targetHandle}
|
||||||
markerEnd={markerEndUrl}
|
markerStart={markerStartUrl}
|
||||||
/>
|
markerEnd={markerEndUrl}
|
||||||
</g>
|
/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
const {
|
const {
|
||||||
elementsSelectable,
|
elementsSelectable,
|
||||||
edgeTree,
|
visibleEdges,
|
||||||
edges: { setDefaultOptions }
|
edges: { setDefaultOptions }
|
||||||
} = useStore();
|
} = useStore();
|
||||||
|
|
||||||
@@ -18,57 +18,47 @@
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#each $edgeTree as group (group.level)}
|
<div class="svelte-flow__edges">
|
||||||
<svg style="z-index: {group.level}" class="svelte-flow__edges">
|
<svg class="svelte-flow__container">
|
||||||
{#if group.isMaxLevel} <MarkerDefinition />{/if}
|
<MarkerDefinition />
|
||||||
<g>
|
|
||||||
{#each group.edges as edge (edge.id)}
|
|
||||||
{@const edgeType = edge.type || 'default'}
|
|
||||||
{@const selectable = !!(
|
|
||||||
edge.selectable ||
|
|
||||||
($elementsSelectable && typeof edge.selectable === 'undefined')
|
|
||||||
)}
|
|
||||||
|
|
||||||
<EdgeWrapper
|
|
||||||
id={edge.id}
|
|
||||||
source={edge.source}
|
|
||||||
target={edge.target}
|
|
||||||
data={edge.data}
|
|
||||||
style={edge.style}
|
|
||||||
animated={edge.animated}
|
|
||||||
selected={edge.selected}
|
|
||||||
hidden={edge.hidden}
|
|
||||||
label={edge.label}
|
|
||||||
labelStyle={edge.labelStyle}
|
|
||||||
markerStart={edge.markerStart}
|
|
||||||
markerEnd={edge.markerEnd}
|
|
||||||
sourceHandle={edge.sourceHandle}
|
|
||||||
targetHandle={edge.targetHandle}
|
|
||||||
sourceX={edge.sourceX}
|
|
||||||
sourceY={edge.sourceY}
|
|
||||||
targetX={edge.targetX}
|
|
||||||
targetY={edge.targetY}
|
|
||||||
sourcePosition={edge.sourcePosition}
|
|
||||||
targetPosition={edge.targetPosition}
|
|
||||||
ariaLabel={edge.ariaLabel}
|
|
||||||
interactionWidth={edge.interactionWidth}
|
|
||||||
class={edge.class}
|
|
||||||
type={edgeType}
|
|
||||||
{selectable}
|
|
||||||
on:edgeclick
|
|
||||||
on:edgecontextmenu
|
|
||||||
/>
|
|
||||||
{/each}
|
|
||||||
</g>
|
|
||||||
</svg>
|
</svg>
|
||||||
{/each}
|
|
||||||
|
|
||||||
<style>
|
{#each $visibleEdges as edge (edge.id)}
|
||||||
.svelte-flow__edges {
|
{@const edgeType = edge.type || 'default'}
|
||||||
width: 100%;
|
{@const selectable = !!(
|
||||||
height: 100%;
|
edge.selectable ||
|
||||||
position: absolute;
|
($elementsSelectable && typeof edge.selectable === 'undefined')
|
||||||
top: 0;
|
)}
|
||||||
left: 0;
|
|
||||||
}
|
<EdgeWrapper
|
||||||
</style>
|
id={edge.id}
|
||||||
|
source={edge.source}
|
||||||
|
target={edge.target}
|
||||||
|
data={edge.data}
|
||||||
|
style={edge.style}
|
||||||
|
animated={edge.animated}
|
||||||
|
selected={edge.selected}
|
||||||
|
hidden={edge.hidden}
|
||||||
|
label={edge.label}
|
||||||
|
labelStyle={edge.labelStyle}
|
||||||
|
markerStart={edge.markerStart}
|
||||||
|
markerEnd={edge.markerEnd}
|
||||||
|
sourceHandle={edge.sourceHandle}
|
||||||
|
targetHandle={edge.targetHandle}
|
||||||
|
sourceX={edge.sourceX}
|
||||||
|
sourceY={edge.sourceY}
|
||||||
|
targetX={edge.targetX}
|
||||||
|
targetY={edge.targetY}
|
||||||
|
sourcePosition={edge.sourcePosition}
|
||||||
|
targetPosition={edge.targetPosition}
|
||||||
|
ariaLabel={edge.ariaLabel}
|
||||||
|
interactionWidth={edge.interactionWidth}
|
||||||
|
class={edge.class}
|
||||||
|
type={edgeType}
|
||||||
|
zIndex={edge.zIndex}
|
||||||
|
{selectable}
|
||||||
|
on:edgeclick
|
||||||
|
on:edgecontextmenu
|
||||||
|
/>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ import type { EdgeTypes, NodeTypes, Node, Edge, FitViewOptions, ConnectionData }
|
|||||||
import { initialEdgeTypes, initialNodeTypes, getInitialStore } from './initial-store';
|
import { initialEdgeTypes, initialNodeTypes, getInitialStore } from './initial-store';
|
||||||
import type { SvelteFlowStore } from './types';
|
import type { SvelteFlowStore } from './types';
|
||||||
import { syncNodeStores, syncEdgeStores, syncViewportStores } from './utils';
|
import { syncNodeStores, syncEdgeStores, syncViewportStores } from './utils';
|
||||||
import { getEdgeTree } from './edge-tree';
|
import { getVisibleEdges } from './visible-edges';
|
||||||
import { getVisibleNodes } from './visible-nodes';
|
import { getVisibleNodes } from './visible-nodes';
|
||||||
import { getDerivedConnectionProps } from './derived-connection-props';
|
import { getDerivedConnectionProps } from './derived-connection-props';
|
||||||
|
|
||||||
@@ -349,8 +349,8 @@ export function createStore({
|
|||||||
...store,
|
...store,
|
||||||
|
|
||||||
// derived state
|
// derived state
|
||||||
edgeTree: getEdgeTree(store),
|
|
||||||
connection: getDerivedConnectionProps(store, currentConnection),
|
connection: getDerivedConnectionProps(store, currentConnection),
|
||||||
|
visibleEdges: getVisibleEdges(store),
|
||||||
visibleNodes: getVisibleNodes(store),
|
visibleNodes: getVisibleNodes(store),
|
||||||
markers: derived(
|
markers: derived(
|
||||||
[store.edges, store.defaultMarkerColor, store.flowId],
|
[store.edges, store.defaultMarkerColor, store.flowId],
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ export const getInitialStore = ({
|
|||||||
nodeLookup: readable<Map<string, Node>>(nodeLookup),
|
nodeLookup: readable<Map<string, Node>>(nodeLookup),
|
||||||
visibleNodes: readable<Node[]>([]),
|
visibleNodes: readable<Node[]>([]),
|
||||||
edges: createEdgesStore(edges),
|
edges: createEdgesStore(edges),
|
||||||
edgeTree: readable<GroupedEdges<EdgeLayouted>[]>([]),
|
visibleEdges: readable<EdgeLayouted[]>([]),
|
||||||
height: writable<number>(500),
|
height: writable<number>(500),
|
||||||
width: writable<number>(500),
|
width: writable<number>(500),
|
||||||
minZoom: writable<number>(0.5),
|
minZoom: writable<number>(0.5),
|
||||||
|
|||||||
+13
-8
@@ -1,10 +1,10 @@
|
|||||||
import { derived } from 'svelte/store';
|
import { derived } from 'svelte/store';
|
||||||
import { groupEdgesByZLevel, isEdgeVisible, getEdgePosition } from '@xyflow/system';
|
import { isEdgeVisible, getEdgePosition, adjustEdgeZIndex } 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';
|
||||||
|
|
||||||
export function getEdgeTree(store: SvelteFlowStoreState) {
|
export function getVisibleEdges(store: SvelteFlowStoreState) {
|
||||||
const visibleEdges = derived(
|
const visibleEdges = derived(
|
||||||
[
|
[
|
||||||
store.edges,
|
store.edges,
|
||||||
@@ -41,8 +41,15 @@ export function getEdgeTree(store: SvelteFlowStoreState) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return derived(
|
return derived(
|
||||||
[visibleEdges, store.nodes, store.nodeLookup, store.connectionMode, store.onerror],
|
[
|
||||||
([visibleEdges, , nodeLookup, connectionMode, onerror]) => {
|
visibleEdges,
|
||||||
|
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);
|
||||||
@@ -63,7 +70,7 @@ export function getEdgeTree(store: SvelteFlowStoreState) {
|
|||||||
|
|
||||||
if (edgePosition) {
|
if (edgePosition) {
|
||||||
res.push({
|
res.push({
|
||||||
...edge,
|
...adjustEdgeZIndex(edge, nodeLookup, onlyRenderVisibleElements),
|
||||||
...edgePosition
|
...edgePosition
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -71,9 +78,7 @@ export function getEdgeTree(store: SvelteFlowStoreState) {
|
|||||||
return res;
|
return res;
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const groupedEdges = groupEdgesByZLevel<EdgeLayouted>(layoutedEdges, nodeLookup, false);
|
return layoutedEdges;
|
||||||
|
|
||||||
return groupedEdges;
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -70,6 +70,7 @@ export type EdgeLayouted = Pick<
|
|||||||
| 'ariaLabel'
|
| 'ariaLabel'
|
||||||
| 'hidden'
|
| 'hidden'
|
||||||
| 'class'
|
| 'class'
|
||||||
|
| 'zIndex'
|
||||||
> &
|
> &
|
||||||
EdgePosition & {
|
EdgePosition & {
|
||||||
sourceNode?: Node;
|
sourceNode?: Node;
|
||||||
|
|||||||
@@ -57,11 +57,6 @@
|
|||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.xy-flow .xy-flow__edges {
|
|
||||||
pointer-events: none;
|
|
||||||
overflow: visible;
|
|
||||||
}
|
|
||||||
|
|
||||||
.xy-flow__edge-path {
|
.xy-flow__edge-path {
|
||||||
stroke: var(--edge-stroke, var(--edge-stroke-default));
|
stroke: var(--edge-stroke, var(--edge-stroke-default));
|
||||||
stroke-width: var(--edge-stroke-width, var(--edge-stroke-width-default));
|
stroke-width: var(--edge-stroke-width, var(--edge-stroke-width-default));
|
||||||
|
|||||||
@@ -20,6 +20,12 @@
|
|||||||
--controls-box-shadow-default: 0 0 2px 1px rgba(0, 0, 0, 0.08);
|
--controls-box-shadow-default: 0 0 2px 1px rgba(0, 0, 0, 0.08);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.xy-flow__edgewrapper {
|
||||||
|
overflow: visible;
|
||||||
|
position: absolute;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
.xy-flow__edge {
|
.xy-flow__edge {
|
||||||
&.updating {
|
&.updating {
|
||||||
.xy-flow__edge-path {
|
.xy-flow__edge-path {
|
||||||
|
|||||||
@@ -23,63 +23,32 @@ export function getEdgeCenter({
|
|||||||
return [centerX, centerY, xOffset, yOffset];
|
return [centerX, centerY, xOffset, yOffset];
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultEdgeTree = [{ level: 0, isMaxLevel: true, edges: [] }];
|
|
||||||
|
|
||||||
export type GroupedEdges<EdgeType extends EdgeBase> = {
|
export type GroupedEdges<EdgeType extends EdgeBase> = {
|
||||||
edges: EdgeType[];
|
edges: EdgeType[];
|
||||||
level: number;
|
level: number;
|
||||||
isMaxLevel: boolean;
|
isMaxLevel: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function groupEdgesByZLevel<EdgeType extends EdgeBase>(
|
export function adjustEdgeZIndex(edge: EdgeBase, nodeLookup: Map<string, NodeBase>, elevateEdgesOnSelect: boolean) {
|
||||||
edges: EdgeType[],
|
const hasZIndex = isNumeric(edge.zIndex);
|
||||||
nodeLookup: Map<string, NodeBase>,
|
let z = hasZIndex ? edge.zIndex! : 0;
|
||||||
elevateEdgesOnSelect = false
|
|
||||||
): GroupedEdges<EdgeType>[] {
|
|
||||||
let maxLevel = -1;
|
|
||||||
|
|
||||||
const levelLookup = edges.reduce<Record<string, EdgeType[]>>((tree, edge) => {
|
if (elevateEdgesOnSelect) {
|
||||||
const hasZIndex = isNumeric(edge.zIndex);
|
const targetNode = nodeLookup.get(edge.target)!;
|
||||||
let z = hasZIndex ? edge.zIndex! : 0;
|
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);
|
||||||
|
z = (hasZIndex ? edge.zIndex! : 0) + (edgeOrConnectedNodeSelected ? selectedZIndex : 0);
|
||||||
|
|
||||||
if (elevateEdgesOnSelect) {
|
if (edge.zIndex !== z) {
|
||||||
const targetNode = nodeLookup.get(edge.target);
|
return {
|
||||||
const sourceNode = nodeLookup.get(edge.source);
|
...edge,
|
||||||
const edgeOrConnectedNodeSelected = edge.selected || targetNode?.selected || sourceNode?.selected;
|
zIndex: z,
|
||||||
const selectedZIndex = Math.max(
|
};
|
||||||
sourceNode?.[internalsSymbol]?.z || 0,
|
|
||||||
targetNode?.[internalsSymbol]?.z || 0,
|
|
||||||
1000
|
|
||||||
);
|
|
||||||
z = (hasZIndex ? edge.zIndex! : 0) + (edgeOrConnectedNodeSelected ? selectedZIndex : 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tree[z]) {
|
|
||||||
tree[z].push(edge);
|
|
||||||
} else {
|
|
||||||
tree[z] = [edge];
|
|
||||||
}
|
|
||||||
|
|
||||||
maxLevel = z > maxLevel ? z : maxLevel;
|
|
||||||
|
|
||||||
return tree;
|
|
||||||
}, {});
|
|
||||||
|
|
||||||
const edgeTree = Object.entries(levelLookup).map(([key, edges]) => {
|
|
||||||
const level = +key;
|
|
||||||
|
|
||||||
return {
|
|
||||||
edges,
|
|
||||||
level,
|
|
||||||
isMaxLevel: level === maxLevel,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
if (edgeTree.length === 0) {
|
|
||||||
return defaultEdgeTree;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return edgeTree;
|
return edge;
|
||||||
}
|
}
|
||||||
|
|
||||||
type IsEdgeVisibleParams = {
|
type IsEdgeVisibleParams = {
|
||||||
|
|||||||
Reference in New Issue
Block a user