feat(svelte): edge marker support

This commit is contained in:
moklick
2023-05-15 16:15:42 +02:00
parent 24400d259b
commit 425f625020
22 changed files with 236 additions and 60 deletions
@@ -4,14 +4,26 @@
type $$Props = BaseEdgeProps;
export let id: $$Props['id'] = undefined;
export let path: $$Props['path'] = '';
export let label: $$Props['label'] = undefined;
export let labelX: $$Props['labelX'] = 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;
</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}
<path
@@ -1,7 +1,10 @@
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;
labelX?: number;
labelY?: number;
markerStart?: string;
markerEnd?: string;
};
@@ -1,6 +1,7 @@
<script lang="ts">
import { createEventDispatcher, type SvelteComponentTyped } from 'svelte';
import { Position } from '@reactflow/system';
import { getMarkerId } from '@reactflow/utils';
import { useStore } from '$lib/store';
import BezierEdge from '$lib/components/edges/BezierEdge.svelte';
@@ -16,20 +17,28 @@
export let sourceY: $$Props['sourceY'] = 0;
export let targetX: $$Props['targetX'] = 0;
export let targetY: $$Props['targetY'] = 0;
// @ todo: support edge updates
// export let sourceHandleId: $$Props['sourceHandleId'] = undefined;
// export let targetHandleId: $$Props['targetHandleId'] = undefined;
export let data: $$Props['data'] = {};
export let style: $$Props['style'] = undefined;
export let sourcePosition: $$Props['sourcePosition'] = Position.Bottom;
export let targetPosition: $$Props['targetPosition'] = Position.Top;
export let animated: $$Props['animated'] = false;
export let selected: $$Props['selected'] = false;
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 edgeComponent: typeof SvelteComponentTyped<EdgeProps> = $edgeTypes[type!] || BezierEdge;
$: markerStartUrl = markerStart ? `url(#${getMarkerId(markerStart, $flowId)})` : undefined;
$: markerEndUrl = markerEnd ? `url(#${getMarkerId(markerEnd, $flowId)})` : undefined;
function onClick() {
const edge = $edges.find(e => e.id === id);
dispatch('edge:click', edge);
@@ -57,6 +66,12 @@
{animated}
{selected}
{label}
{data}
{style}
{sourceHandleId}
{targetHandleId}
markerStart={markerStartUrl}
markerEnd={markerEndUrl}
/>
</g>
@@ -16,4 +16,15 @@
});
</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>
<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>
<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>
<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">
import { EdgeWrapper } from '$lib/components/EdgeWrapper';
import { MarkerDefinition } from '$lib/container/EdgeRenderer/MarkerDefinition';
import { useStore } from '$lib/store';
const { edgesLayouted, width, height } = useStore();
@@ -9,8 +10,11 @@
{#each $edgesLayouted as edge (edge.id)}
<EdgeWrapper {...edge} on:edge:click />
{/each}
<MarkerDefinition />
</svg>
<style>
.svelte-flow__edges {
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>
@@ -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;
export let id: $$Props['id'] = '1';
export let id = '1';
export let fitView: $$Props['fitView'] = undefined;
export let minZoom: $$Props['minZoom'] = undefined;
export let maxZoom: $$Props['maxZoom'] = undefined;
@@ -31,13 +31,14 @@
export let connectionRadius: $$Props['connectionRadius'] = undefined;
export let connectionLineType: $$Props['connectionLineType'] = undefined;
export let isValidConnection: $$Props['isValidConnection'] = undefined;
export let defaultMarkerColor = '#b1b1b7';
export let style: $$Props['style'] = undefined;
let className: $$Props['class'] = undefined;
export { className as class };
let domNode: HTMLDivElement;
$: flowId = id;
const store = useStore();
onMount(() => {
@@ -53,12 +54,13 @@
$: {
const updatableProps = {
id,
flowId,
connectionLineType,
connectionRadius,
selectionMode,
snapGrid,
isValidConnection
isValidConnection,
defaultMarkerColor
};
Object.keys(updatableProps).forEach(prop => {
@@ -32,6 +32,7 @@ export type SvelteFlowProps = DOMAttributes<HTMLDivElement> & {
connectionRadius?: number;
selectionMode?: SelectionMode;
snapGrid?: SnapGrid;
defaultMarkerColor?: string;
class?: string;
style?: string;
@@ -31,14 +31,14 @@
let className: $$Props['class'] = '';
export { className as class };
const { transform, id } = useStore();
const { transform, flowId } = useStore();
const patternColor = color || defaultColor[variant!];
const patternSize = size || defaultSize[variant!];
const isDots = variant === BackgroundVariant.Dots;
const isCross = variant === BackgroundVariant.Cross;
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];
$: scaledSize = patternSize * $transform[2];
$: patternDimensions = (isCross ? [scaledSize, scaledSize] : scaledGap) as [number, number];
+8 -1
View File
@@ -1,5 +1,5 @@
import { getContext } from 'svelte';
import { get } from 'svelte/store';
import { derived, get } from 'svelte/store';
import { zoomIdentity } from 'd3-zoom';
import {
type NodeDragItem,
@@ -11,6 +11,7 @@ import {
type CoordinateExtent
} from '@reactflow/system';
import {
createMarkerIds,
fitView as fitViewUtil,
getD3Transition,
getDimensions,
@@ -314,6 +315,12 @@ export function createStore(params: CreateStoreParams): SvelteFlowStore {
// derived state
edgesLayouted: getEdgesLayouted(store),
connectionPath: getConnectionPath(store),
markers: derived(
[store.edges, store.defaultMarkerColor, store.flowId],
([edges, defaultColor, id]) => {
return createMarkerIds(edges, { defaultColor, id });
}
),
// actions
setNodeTypes,
@@ -7,7 +7,8 @@ import {
ConnectionLineType,
type SelectionRect,
type Transform,
type SnapGrid
type SnapGrid,
type MarkerProps
} from '@reactflow/system';
import DefaultNode from '$lib/components/nodes/DefaultNode.svelte';
@@ -49,7 +50,7 @@ export const initialEdgeTypes = {
};
export const initialStoreState = {
id: writable<string | null>(null),
flowId: writable<string | null>(null),
nodes: writable<Node[]>([]),
edges: writable<Edge[]>([]),
edgesLayouted: readable<EdgeLayouted[]>([]),
@@ -80,5 +81,7 @@ export const initialStoreState = {
connection: writable<ConnectionData>(initConnectionData),
connectionRadius: writable<number>(25),
connectionLineType: writable<ConnectionLineType>(ConnectionLineType.Bezier),
isValidConnection: writable<IsValidConnection>(() => true)
isValidConnection: writable<IsValidConnection>(() => true),
markers: readable<MarkerProps[]>([]),
defaultMarkerColor: writable<string>('#b1b1b7')
};
+8
View File
@@ -47,11 +47,15 @@ export type EdgeLayouted = Omit<Edge, 'sourceHandle' | 'targetHandle'> & {
targetPosition: Position;
sourceHandleId?: string;
targetHandleId?: string;
markerStart?: string;
markerEnd?: string;
};
export type EdgeProps = Pick<
EdgeLayouted,
| 'id'
| 'data'
| 'style'
| 'source'
| 'target'
| 'sourceX'
@@ -64,6 +68,10 @@ export type EdgeProps = Pick<
| 'selected'
| 'label'
| 'interactionWidth'
| 'markerStart'
| 'markerEnd'
| 'sourceHandleId'
| 'targetHandleId'
>;
export type EdgeTypes = Record<string, typeof SvelteComponentTyped<EdgeProps>>;