feat(svelte): edge marker support
This commit is contained in:
@@ -1,15 +1,11 @@
|
|||||||
import { memo, useCallback } from 'react';
|
import { memo, useCallback } from 'react';
|
||||||
import type { EdgeMarker } from '@reactflow/system';
|
import type { MarkerProps } from '@reactflow/system';
|
||||||
import { getMarkerId } from '@reactflow/utils';
|
import { createMarkerIds } from '@reactflow/utils';
|
||||||
|
|
||||||
import { useStore } from '../../hooks/useStore';
|
import { useStore } from '../../hooks/useStore';
|
||||||
import { useMarkerSymbol } from './MarkerSymbols';
|
import { useMarkerSymbol } from './MarkerSymbols';
|
||||||
import type { ReactFlowState } from '../../types';
|
import type { ReactFlowState } from '../../types';
|
||||||
|
|
||||||
type MarkerProps = EdgeMarker & {
|
|
||||||
id: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
type MarkerDefinitionsProps = {
|
type MarkerDefinitionsProps = {
|
||||||
defaultColor: string;
|
defaultColor: string;
|
||||||
rfId?: string;
|
rfId?: string;
|
||||||
@@ -51,22 +47,9 @@ const Marker = ({
|
|||||||
const markerSelector =
|
const markerSelector =
|
||||||
({ defaultColor, rfId }: { defaultColor: string; rfId?: string }) =>
|
({ defaultColor, rfId }: { defaultColor: string; rfId?: string }) =>
|
||||||
(s: ReactFlowState) => {
|
(s: ReactFlowState) => {
|
||||||
const ids: string[] = [];
|
const markers = createMarkerIds(s.edges, { id: rfId, defaultColor });
|
||||||
|
|
||||||
return s.edges
|
return markers;
|
||||||
.reduce<MarkerProps[]>((markers, edge) => {
|
|
||||||
[edge.markerStart, edge.markerEnd].forEach((marker) => {
|
|
||||||
if (marker && typeof marker === 'object') {
|
|
||||||
const markerId = getMarkerId(marker, rfId);
|
|
||||||
if (!ids.includes(markerId)) {
|
|
||||||
markers.push({ id: markerId, color: marker.color || defaultColor, ...marker });
|
|
||||||
ids.push(markerId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return markers;
|
|
||||||
}, [])
|
|
||||||
.sort((a, b) => a.id.localeCompare(b.id));
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// when you have multiple flows on a page and you hide the first one, the other ones have no markers anymore
|
// when you have multiple flows on a page and you hide the first one, the other ones have no markers anymore
|
||||||
|
|||||||
@@ -4,14 +4,26 @@
|
|||||||
|
|
||||||
type $$Props = BaseEdgeProps;
|
type $$Props = BaseEdgeProps;
|
||||||
|
|
||||||
|
export let id: $$Props['id'] = undefined;
|
||||||
export let path: $$Props['path'] = '';
|
export let path: $$Props['path'] = '';
|
||||||
export let label: $$Props['label'] = undefined;
|
export let label: $$Props['label'] = undefined;
|
||||||
export let labelX: $$Props['labelX'] = undefined;
|
export let labelX: $$Props['labelX'] = undefined;
|
||||||
export let labelY: $$Props['labelY'] = undefined;
|
export let labelY: $$Props['labelY'] = undefined;
|
||||||
|
export let markerStart: $$Props['markerStart'] = undefined;
|
||||||
|
export let markerEnd: $$Props['markerEnd'] = undefined;
|
||||||
|
export let style: $$Props['style'] = undefined;
|
||||||
export let interactionWidth: $$Props['interactionWidth'] = 20;
|
export let interactionWidth: $$Props['interactionWidth'] = 20;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<path d={path} fill="none" class="svelte-flow__edge-path" />
|
<path
|
||||||
|
d={path}
|
||||||
|
id={id}
|
||||||
|
fill="none"
|
||||||
|
class="svelte-flow__edge-path"
|
||||||
|
marker-start={markerStart}
|
||||||
|
marker-end={markerEnd}
|
||||||
|
style={style}
|
||||||
|
/>
|
||||||
|
|
||||||
{#if interactionWidth}
|
{#if interactionWidth}
|
||||||
<path
|
<path
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
import type { EdgeProps } from '$lib/types';
|
import type { EdgeProps } from '$lib/types';
|
||||||
|
|
||||||
export type BaseEdgeProps = Pick<EdgeProps, 'interactionWidth' | 'label'> & {
|
export type BaseEdgeProps = Pick<EdgeProps, 'interactionWidth' | 'label' | 'style'> & {
|
||||||
|
id?: string;
|
||||||
path: string;
|
path: string;
|
||||||
labelX?: number;
|
labelX?: number;
|
||||||
labelY?: number;
|
labelY?: number;
|
||||||
|
markerStart?: string;
|
||||||
|
markerEnd?: string;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { createEventDispatcher, type SvelteComponentTyped } from 'svelte';
|
import { createEventDispatcher, type SvelteComponentTyped } from 'svelte';
|
||||||
import { Position } from '@reactflow/system';
|
import { Position } from '@reactflow/system';
|
||||||
|
import { getMarkerId } from '@reactflow/utils';
|
||||||
|
|
||||||
import { useStore } from '$lib/store';
|
import { useStore } from '$lib/store';
|
||||||
import BezierEdge from '$lib/components/edges/BezierEdge.svelte';
|
import BezierEdge from '$lib/components/edges/BezierEdge.svelte';
|
||||||
@@ -16,20 +17,28 @@
|
|||||||
export let sourceY: $$Props['sourceY'] = 0;
|
export let sourceY: $$Props['sourceY'] = 0;
|
||||||
export let targetX: $$Props['targetX'] = 0;
|
export let targetX: $$Props['targetX'] = 0;
|
||||||
export let targetY: $$Props['targetY'] = 0;
|
export let targetY: $$Props['targetY'] = 0;
|
||||||
// @ todo: support edge updates
|
export let data: $$Props['data'] = {};
|
||||||
// export let sourceHandleId: $$Props['sourceHandleId'] = undefined;
|
export let style: $$Props['style'] = undefined;
|
||||||
// export let targetHandleId: $$Props['targetHandleId'] = undefined;
|
|
||||||
export let sourcePosition: $$Props['sourcePosition'] = Position.Bottom;
|
export let sourcePosition: $$Props['sourcePosition'] = Position.Bottom;
|
||||||
export let targetPosition: $$Props['targetPosition'] = Position.Top;
|
export let targetPosition: $$Props['targetPosition'] = Position.Top;
|
||||||
export let animated: $$Props['animated'] = false;
|
export let animated: $$Props['animated'] = false;
|
||||||
export let selected: $$Props['selected'] = false;
|
export let selected: $$Props['selected'] = false;
|
||||||
export let label: $$Props['label'] = undefined;
|
export let label: $$Props['label'] = undefined;
|
||||||
|
export let markerStart: $$Props['markerStart'] = undefined;
|
||||||
|
export let markerEnd: $$Props['markerEnd'] = undefined;
|
||||||
|
export let sourceHandleId: $$Props['sourceHandleId'] = undefined;
|
||||||
|
export let targetHandleId: $$Props['targetHandleId'] = undefined;
|
||||||
|
|
||||||
const { edges, edgeTypes } = useStore();
|
// @ todo: support edge updates
|
||||||
|
|
||||||
|
const { edges, edgeTypes, flowId } = useStore();
|
||||||
const dispatch = createEventDispatcher();
|
const dispatch = createEventDispatcher();
|
||||||
|
|
||||||
const edgeComponent: typeof SvelteComponentTyped<EdgeProps> = $edgeTypes[type!] || BezierEdge;
|
const edgeComponent: typeof SvelteComponentTyped<EdgeProps> = $edgeTypes[type!] || BezierEdge;
|
||||||
|
|
||||||
|
$: markerStartUrl = markerStart ? `url(#${getMarkerId(markerStart, $flowId)})` : undefined;
|
||||||
|
$: markerEndUrl = markerEnd ? `url(#${getMarkerId(markerEnd, $flowId)})` : undefined;
|
||||||
|
|
||||||
function onClick() {
|
function onClick() {
|
||||||
const edge = $edges.find(e => e.id === id);
|
const edge = $edges.find(e => e.id === id);
|
||||||
dispatch('edge:click', edge);
|
dispatch('edge:click', edge);
|
||||||
@@ -57,6 +66,12 @@
|
|||||||
{animated}
|
{animated}
|
||||||
{selected}
|
{selected}
|
||||||
{label}
|
{label}
|
||||||
|
{data}
|
||||||
|
{style}
|
||||||
|
{sourceHandleId}
|
||||||
|
{targetHandleId}
|
||||||
|
markerStart={markerStartUrl}
|
||||||
|
markerEnd={markerEndUrl}
|
||||||
/>
|
/>
|
||||||
</g>
|
</g>
|
||||||
|
|
||||||
|
|||||||
@@ -16,4 +16,15 @@
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<BaseEdge {path} {labelX} {labelY} {...$$props} />
|
<BaseEdge
|
||||||
|
{path}
|
||||||
|
{labelX}
|
||||||
|
{labelY}
|
||||||
|
id={$$props.id}
|
||||||
|
label={$$props.label}
|
||||||
|
markerStart={$$props.markerStart}
|
||||||
|
markerEnd={$$props.markerEnd}
|
||||||
|
interactionWidth={$$props.interactionWidth}
|
||||||
|
style={$$props.style}
|
||||||
|
/>
|
||||||
|
|
||||||
|
|||||||
@@ -16,4 +16,14 @@
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<BaseEdge {path} {labelX} {labelY} {...$$props} />
|
<BaseEdge
|
||||||
|
{path}
|
||||||
|
{labelX}
|
||||||
|
{labelY}
|
||||||
|
id={$$props.id}
|
||||||
|
label={$$props.label}
|
||||||
|
markerStart={$$props.markerStart}
|
||||||
|
markerEnd={$$props.markerEnd}
|
||||||
|
interactionWidth={$$props.interactionWidth}
|
||||||
|
style={$$props.style}
|
||||||
|
/>
|
||||||
|
|||||||
@@ -17,4 +17,14 @@
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<BaseEdge {path} {labelX} {labelY} {...$$props} />
|
<BaseEdge
|
||||||
|
{path}
|
||||||
|
{labelX}
|
||||||
|
{labelY}
|
||||||
|
id={$$props.id}
|
||||||
|
label={$$props.label}
|
||||||
|
markerStart={$$props.markerStart}
|
||||||
|
markerEnd={$$props.markerEnd}
|
||||||
|
interactionWidth={$$props.interactionWidth}
|
||||||
|
style={$$props.style}
|
||||||
|
/>
|
||||||
|
|||||||
@@ -14,4 +14,15 @@
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<BaseEdge {path} {labelX} {labelY} {...$$props} />
|
<BaseEdge
|
||||||
|
{path}
|
||||||
|
{labelX}
|
||||||
|
{labelY}
|
||||||
|
id={$$props.id}
|
||||||
|
label={$$props.label}
|
||||||
|
markerStart={$$props.markerStart}
|
||||||
|
markerEnd={$$props.markerEnd}
|
||||||
|
interactionWidth={$$props.interactionWidth}
|
||||||
|
style={$$props.style}
|
||||||
|
/>
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { EdgeWrapper } from '$lib/components/EdgeWrapper';
|
import { EdgeWrapper } from '$lib/components/EdgeWrapper';
|
||||||
|
import { MarkerDefinition } from '$lib/container/EdgeRenderer/MarkerDefinition';
|
||||||
import { useStore } from '$lib/store';
|
import { useStore } from '$lib/store';
|
||||||
|
|
||||||
const { edgesLayouted, width, height } = useStore();
|
const { edgesLayouted, width, height } = useStore();
|
||||||
@@ -9,8 +10,11 @@
|
|||||||
{#each $edgesLayouted as edge (edge.id)}
|
{#each $edgesLayouted as edge (edge.id)}
|
||||||
<EdgeWrapper {...edge} on:edge:click />
|
<EdgeWrapper {...edge} on:edge:click />
|
||||||
{/each}
|
{/each}
|
||||||
|
|
||||||
|
<MarkerDefinition />
|
||||||
</svg>
|
</svg>
|
||||||
|
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.svelte-flow__edges {
|
.svelte-flow__edges {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { type MarkerProps, MarkerType } from '@reactflow/system';
|
||||||
|
|
||||||
|
type $$Props = MarkerProps;
|
||||||
|
|
||||||
|
export let id: $$Props['id'];
|
||||||
|
export let type: $$Props['type'];
|
||||||
|
export let width: $$Props['width'] = 12.5;
|
||||||
|
export let height: $$Props['height'] = 12.5;
|
||||||
|
export let markerUnits: $$Props['markerUnits'] = 'strokeWidth';
|
||||||
|
export let orient: $$Props['orient']= 'auto-start-reverse';
|
||||||
|
export let color: $$Props['color'] = undefined;
|
||||||
|
export let strokeWidth: $$Props['strokeWidth'] = undefined;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<marker
|
||||||
|
class="svelte-flow__arrowhead"
|
||||||
|
id={id}
|
||||||
|
markerWidth={`${width}`}
|
||||||
|
markerHeight={`${height}`}
|
||||||
|
viewBox="-10 -10 20 20"
|
||||||
|
markerUnits={markerUnits}
|
||||||
|
orient={orient}
|
||||||
|
refX="0"
|
||||||
|
refY="0"
|
||||||
|
>
|
||||||
|
{#if type === MarkerType.Arrow}
|
||||||
|
<polyline
|
||||||
|
stroke={color}
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width={strokeWidth}
|
||||||
|
fill="none"
|
||||||
|
points="-5,-4 0,0 -5,4"
|
||||||
|
/>
|
||||||
|
{:else if type === MarkerType.ArrowClosed}
|
||||||
|
<polyline
|
||||||
|
stroke={color}
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width={strokeWidth}
|
||||||
|
fill={color}
|
||||||
|
points="-5,-4 0,0 -5,4 -5,-4"
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
|
</marker>
|
||||||
+21
@@ -0,0 +1,21 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { useStore } from '$lib/store';
|
||||||
|
import { Marker } from '$lib/container/EdgeRenderer/MarkerDefinition';
|
||||||
|
|
||||||
|
const { markers } = useStore();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<defs>
|
||||||
|
{#each $markers as marker(marker.id)}
|
||||||
|
<Marker
|
||||||
|
id={marker.id}
|
||||||
|
type={marker.type}
|
||||||
|
color={marker.color}
|
||||||
|
width={marker.width}
|
||||||
|
height={marker.height}
|
||||||
|
markerUnits={marker.markerUnits}
|
||||||
|
strokeWidth={marker.strokeWidth}
|
||||||
|
orient={marker.orient}
|
||||||
|
/>
|
||||||
|
{/each}
|
||||||
|
</defs>
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
export { default as MarkerDefinition } from './MarkerDefinition.svelte';
|
||||||
|
export { default as Marker } from './Marker.svelte';
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
|
|
||||||
type $$Props = SvelteFlowProps;
|
type $$Props = SvelteFlowProps;
|
||||||
|
|
||||||
export let id: $$Props['id'] = '1';
|
export let id = '1';
|
||||||
export let fitView: $$Props['fitView'] = undefined;
|
export let fitView: $$Props['fitView'] = undefined;
|
||||||
export let minZoom: $$Props['minZoom'] = undefined;
|
export let minZoom: $$Props['minZoom'] = undefined;
|
||||||
export let maxZoom: $$Props['maxZoom'] = undefined;
|
export let maxZoom: $$Props['maxZoom'] = undefined;
|
||||||
@@ -31,13 +31,14 @@
|
|||||||
export let connectionRadius: $$Props['connectionRadius'] = undefined;
|
export let connectionRadius: $$Props['connectionRadius'] = undefined;
|
||||||
export let connectionLineType: $$Props['connectionLineType'] = undefined;
|
export let connectionLineType: $$Props['connectionLineType'] = undefined;
|
||||||
export let isValidConnection: $$Props['isValidConnection'] = undefined;
|
export let isValidConnection: $$Props['isValidConnection'] = undefined;
|
||||||
|
export let defaultMarkerColor = '#b1b1b7';
|
||||||
|
|
||||||
export let style: $$Props['style'] = undefined;
|
export let style: $$Props['style'] = undefined;
|
||||||
let className: $$Props['class'] = undefined;
|
let className: $$Props['class'] = undefined;
|
||||||
export { className as class };
|
export { className as class };
|
||||||
|
|
||||||
let domNode: HTMLDivElement;
|
let domNode: HTMLDivElement;
|
||||||
|
|
||||||
|
$: flowId = id;
|
||||||
const store = useStore();
|
const store = useStore();
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
@@ -53,12 +54,13 @@
|
|||||||
|
|
||||||
$: {
|
$: {
|
||||||
const updatableProps = {
|
const updatableProps = {
|
||||||
id,
|
flowId,
|
||||||
connectionLineType,
|
connectionLineType,
|
||||||
connectionRadius,
|
connectionRadius,
|
||||||
selectionMode,
|
selectionMode,
|
||||||
snapGrid,
|
snapGrid,
|
||||||
isValidConnection
|
isValidConnection,
|
||||||
|
defaultMarkerColor
|
||||||
};
|
};
|
||||||
|
|
||||||
Object.keys(updatableProps).forEach(prop => {
|
Object.keys(updatableProps).forEach(prop => {
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ export type SvelteFlowProps = DOMAttributes<HTMLDivElement> & {
|
|||||||
connectionRadius?: number;
|
connectionRadius?: number;
|
||||||
selectionMode?: SelectionMode;
|
selectionMode?: SelectionMode;
|
||||||
snapGrid?: SnapGrid;
|
snapGrid?: SnapGrid;
|
||||||
|
defaultMarkerColor?: string;
|
||||||
|
|
||||||
class?: string;
|
class?: string;
|
||||||
style?: string;
|
style?: string;
|
||||||
|
|||||||
@@ -31,14 +31,14 @@
|
|||||||
let className: $$Props['class'] = '';
|
let className: $$Props['class'] = '';
|
||||||
export { className as class };
|
export { className as class };
|
||||||
|
|
||||||
const { transform, id } = useStore();
|
const { transform, flowId } = useStore();
|
||||||
const patternColor = color || defaultColor[variant!];
|
const patternColor = color || defaultColor[variant!];
|
||||||
const patternSize = size || defaultSize[variant!];
|
const patternSize = size || defaultSize[variant!];
|
||||||
const isDots = variant === BackgroundVariant.Dots;
|
const isDots = variant === BackgroundVariant.Dots;
|
||||||
const isCross = variant === BackgroundVariant.Cross;
|
const isCross = variant === BackgroundVariant.Cross;
|
||||||
const gapXY: number[] = Array.isArray(gap!) ? gap! : [gap!, gap!];
|
const gapXY: number[] = Array.isArray(gap!) ? gap! : [gap!, gap!];
|
||||||
|
|
||||||
$: patternId = `background-pattern-${$id}`;
|
$: patternId = `background-pattern-${$flowId}`;
|
||||||
$: scaledGap = [gapXY[0] * $transform[2] || 1, gapXY[1] * $transform[2] || 1];
|
$: scaledGap = [gapXY[0] * $transform[2] || 1, gapXY[1] * $transform[2] || 1];
|
||||||
$: scaledSize = patternSize * $transform[2];
|
$: scaledSize = patternSize * $transform[2];
|
||||||
$: patternDimensions = (isCross ? [scaledSize, scaledSize] : scaledGap) as [number, number];
|
$: patternDimensions = (isCross ? [scaledSize, scaledSize] : scaledGap) as [number, number];
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { getContext } from 'svelte';
|
import { getContext } from 'svelte';
|
||||||
import { get } from 'svelte/store';
|
import { derived, get } from 'svelte/store';
|
||||||
import { zoomIdentity } from 'd3-zoom';
|
import { zoomIdentity } from 'd3-zoom';
|
||||||
import {
|
import {
|
||||||
type NodeDragItem,
|
type NodeDragItem,
|
||||||
@@ -11,6 +11,7 @@ import {
|
|||||||
type CoordinateExtent
|
type CoordinateExtent
|
||||||
} from '@reactflow/system';
|
} from '@reactflow/system';
|
||||||
import {
|
import {
|
||||||
|
createMarkerIds,
|
||||||
fitView as fitViewUtil,
|
fitView as fitViewUtil,
|
||||||
getD3Transition,
|
getD3Transition,
|
||||||
getDimensions,
|
getDimensions,
|
||||||
@@ -314,6 +315,12 @@ export function createStore(params: CreateStoreParams): SvelteFlowStore {
|
|||||||
// derived state
|
// derived state
|
||||||
edgesLayouted: getEdgesLayouted(store),
|
edgesLayouted: getEdgesLayouted(store),
|
||||||
connectionPath: getConnectionPath(store),
|
connectionPath: getConnectionPath(store),
|
||||||
|
markers: derived(
|
||||||
|
[store.edges, store.defaultMarkerColor, store.flowId],
|
||||||
|
([edges, defaultColor, id]) => {
|
||||||
|
return createMarkerIds(edges, { defaultColor, id });
|
||||||
|
}
|
||||||
|
),
|
||||||
|
|
||||||
// actions
|
// actions
|
||||||
setNodeTypes,
|
setNodeTypes,
|
||||||
|
|||||||
@@ -7,7 +7,8 @@ import {
|
|||||||
ConnectionLineType,
|
ConnectionLineType,
|
||||||
type SelectionRect,
|
type SelectionRect,
|
||||||
type Transform,
|
type Transform,
|
||||||
type SnapGrid
|
type SnapGrid,
|
||||||
|
type MarkerProps
|
||||||
} from '@reactflow/system';
|
} from '@reactflow/system';
|
||||||
|
|
||||||
import DefaultNode from '$lib/components/nodes/DefaultNode.svelte';
|
import DefaultNode from '$lib/components/nodes/DefaultNode.svelte';
|
||||||
@@ -49,7 +50,7 @@ export const initialEdgeTypes = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const initialStoreState = {
|
export const initialStoreState = {
|
||||||
id: writable<string | null>(null),
|
flowId: writable<string | null>(null),
|
||||||
nodes: writable<Node[]>([]),
|
nodes: writable<Node[]>([]),
|
||||||
edges: writable<Edge[]>([]),
|
edges: writable<Edge[]>([]),
|
||||||
edgesLayouted: readable<EdgeLayouted[]>([]),
|
edgesLayouted: readable<EdgeLayouted[]>([]),
|
||||||
@@ -80,5 +81,7 @@ export const initialStoreState = {
|
|||||||
connection: writable<ConnectionData>(initConnectionData),
|
connection: writable<ConnectionData>(initConnectionData),
|
||||||
connectionRadius: writable<number>(25),
|
connectionRadius: writable<number>(25),
|
||||||
connectionLineType: writable<ConnectionLineType>(ConnectionLineType.Bezier),
|
connectionLineType: writable<ConnectionLineType>(ConnectionLineType.Bezier),
|
||||||
isValidConnection: writable<IsValidConnection>(() => true)
|
isValidConnection: writable<IsValidConnection>(() => true),
|
||||||
|
markers: readable<MarkerProps[]>([]),
|
||||||
|
defaultMarkerColor: writable<string>('#b1b1b7')
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -47,11 +47,15 @@ export type EdgeLayouted = Omit<Edge, 'sourceHandle' | 'targetHandle'> & {
|
|||||||
targetPosition: Position;
|
targetPosition: Position;
|
||||||
sourceHandleId?: string;
|
sourceHandleId?: string;
|
||||||
targetHandleId?: string;
|
targetHandleId?: string;
|
||||||
|
markerStart?: string;
|
||||||
|
markerEnd?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type EdgeProps = Pick<
|
export type EdgeProps = Pick<
|
||||||
EdgeLayouted,
|
EdgeLayouted,
|
||||||
| 'id'
|
| 'id'
|
||||||
|
| 'data'
|
||||||
|
| 'style'
|
||||||
| 'source'
|
| 'source'
|
||||||
| 'target'
|
| 'target'
|
||||||
| 'sourceX'
|
| 'sourceX'
|
||||||
@@ -64,6 +68,10 @@ export type EdgeProps = Pick<
|
|||||||
| 'selected'
|
| 'selected'
|
||||||
| 'label'
|
| 'label'
|
||||||
| 'interactionWidth'
|
| 'interactionWidth'
|
||||||
|
| 'markerStart'
|
||||||
|
| 'markerEnd'
|
||||||
|
| 'sourceHandleId'
|
||||||
|
| 'targetHandleId'
|
||||||
>;
|
>;
|
||||||
|
|
||||||
export type EdgeTypes = Record<string, typeof SvelteComponentTyped<EdgeProps>>;
|
export type EdgeTypes = Record<string, typeof SvelteComponentTyped<EdgeProps>>;
|
||||||
|
|||||||
@@ -57,3 +57,7 @@ export enum MarkerType {
|
|||||||
Arrow = 'arrow',
|
Arrow = 'arrow',
|
||||||
ArrowClosed = 'arrowclosed',
|
ArrowClosed = 'arrowclosed',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type MarkerProps = EdgeMarker & {
|
||||||
|
id: string;
|
||||||
|
};
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import { boxToRect, clamp, devWarn, getBoundsOfBoxes, getOverlappingArea, rectTo
|
|||||||
import {
|
import {
|
||||||
errorMessages,
|
errorMessages,
|
||||||
type Connection,
|
type Connection,
|
||||||
type EdgeMarkerType,
|
|
||||||
type Transform,
|
type Transform,
|
||||||
type XYPosition,
|
type XYPosition,
|
||||||
type Rect,
|
type Rect,
|
||||||
@@ -54,23 +53,6 @@ export const getIncomersBase = <NodeType extends BaseNode = BaseNode, EdgeType e
|
|||||||
const getEdgeId = ({ source, sourceHandle, target, targetHandle }: Connection | BaseEdge): string =>
|
const getEdgeId = ({ source, sourceHandle, target, targetHandle }: Connection | BaseEdge): string =>
|
||||||
`reactflow__edge-${source}${sourceHandle || ''}-${target}${targetHandle || ''}`;
|
`reactflow__edge-${source}${sourceHandle || ''}-${target}${targetHandle || ''}`;
|
||||||
|
|
||||||
export const getMarkerId = (marker: EdgeMarkerType | undefined, rfId?: string): string => {
|
|
||||||
if (typeof marker === 'undefined') {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof marker === 'string') {
|
|
||||||
return marker;
|
|
||||||
}
|
|
||||||
|
|
||||||
const idPrefix = rfId ? `${rfId}__` : '';
|
|
||||||
|
|
||||||
return `${idPrefix}${Object.keys(marker)
|
|
||||||
.sort()
|
|
||||||
.map((key: string) => `${key}=${(marker as any)[key]}`)
|
|
||||||
.join('&')}`;
|
|
||||||
};
|
|
||||||
|
|
||||||
const connectionExists = (edge: BaseEdge, edges: BaseEdge[]) => {
|
const connectionExists = (edge: BaseEdge, edges: BaseEdge[]) => {
|
||||||
return edges.some(
|
return edges.some(
|
||||||
(el) =>
|
(el) =>
|
||||||
|
|||||||
@@ -1,2 +1,3 @@
|
|||||||
export * from './graph';
|
export * from './graph';
|
||||||
export * from './utils';
|
export * from './utils';
|
||||||
|
export * from './marker';
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
import type { BaseEdge, EdgeMarker, EdgeMarkerType, MarkerProps } from '@reactflow/system';
|
||||||
|
|
||||||
|
export function getMarkerId(marker: EdgeMarkerType | undefined, id?: string | null): string {
|
||||||
|
if (!marker) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof marker === 'string') {
|
||||||
|
return marker;
|
||||||
|
}
|
||||||
|
|
||||||
|
const idPrefix = id ? `${id}__` : '';
|
||||||
|
|
||||||
|
return `${idPrefix}${Object.keys(marker)
|
||||||
|
.sort()
|
||||||
|
.map((key) => `${key}=${marker[key as keyof EdgeMarker]}`)
|
||||||
|
.join('&')}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createMarkerIds(
|
||||||
|
edges: BaseEdge[],
|
||||||
|
{ id, defaultColor }: { id?: string | null; defaultColor?: string }
|
||||||
|
) {
|
||||||
|
const ids: string[] = [];
|
||||||
|
|
||||||
|
return edges
|
||||||
|
.reduce<MarkerProps[]>((markers, edge) => {
|
||||||
|
[edge.markerStart, edge.markerEnd].forEach((marker) => {
|
||||||
|
if (marker && typeof marker === 'object') {
|
||||||
|
const markerId = getMarkerId(marker, id);
|
||||||
|
if (!ids.includes(markerId)) {
|
||||||
|
markers.push({ id: markerId, color: marker.color || defaultColor, ...marker });
|
||||||
|
ids.push(markerId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return markers;
|
||||||
|
}, [])
|
||||||
|
.sort((a, b) => a.id.localeCompare(b.id));
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user