From 425f6250205cc2f76ad5b557307d0868903763e5 Mon Sep 17 00:00:00 2001 From: moklick Date: Mon, 15 May 2023 16:15:42 +0200 Subject: [PATCH] feat(svelte): edge marker support --- .../EdgeRenderer/MarkerDefinitions.tsx | 25 ++-------- .../lib/components/BaseEdge/BaseEdge.svelte | 14 +++++- .../src/lib/components/BaseEdge/types.ts | 5 +- .../components/EdgeWrapper/EdgeWrapper.svelte | 23 ++++++++-- .../lib/components/edges/BezierEdge.svelte | 13 +++++- .../components/edges/SmoothStepEdge.svelte | 12 ++++- .../src/lib/components/edges/StepEdge.svelte | 12 ++++- .../lib/components/edges/StraightEdge.svelte | 13 +++++- .../EdgeRenderer/EdgeRenderer.svelte | 4 ++ .../MarkerDefinition/Marker.svelte | 46 +++++++++++++++++++ .../MarkerDefinition/MarkerDefinition.svelte | 21 +++++++++ .../EdgeRenderer/MarkerDefinition/index.ts | 2 + .../container/SvelteFlow/SvelteFlow.svelte | 12 +++-- .../src/lib/container/SvelteFlow/types.ts | 1 + .../lib/plugins/Background/Background.svelte | 4 +- packages/svelte/src/lib/store/index.ts | 9 +++- .../svelte/src/lib/store/initial-store.ts | 9 ++-- packages/svelte/src/lib/types/edges.ts | 8 ++++ packages/system/src/types/edges.ts | 4 ++ packages/utils/src/graph.ts | 18 -------- packages/utils/src/index.ts | 1 + packages/utils/src/marker.ts | 40 ++++++++++++++++ 22 files changed, 236 insertions(+), 60 deletions(-) create mode 100644 packages/svelte/src/lib/container/EdgeRenderer/MarkerDefinition/Marker.svelte create mode 100644 packages/svelte/src/lib/container/EdgeRenderer/MarkerDefinition/MarkerDefinition.svelte create mode 100644 packages/svelte/src/lib/container/EdgeRenderer/MarkerDefinition/index.ts create mode 100644 packages/utils/src/marker.ts diff --git a/packages/core/src/container/EdgeRenderer/MarkerDefinitions.tsx b/packages/core/src/container/EdgeRenderer/MarkerDefinitions.tsx index 1ea6296c..2bfdd15d 100644 --- a/packages/core/src/container/EdgeRenderer/MarkerDefinitions.tsx +++ b/packages/core/src/container/EdgeRenderer/MarkerDefinitions.tsx @@ -1,15 +1,11 @@ import { memo, useCallback } from 'react'; -import type { EdgeMarker } from '@reactflow/system'; -import { getMarkerId } from '@reactflow/utils'; +import type { MarkerProps } from '@reactflow/system'; +import { createMarkerIds } from '@reactflow/utils'; import { useStore } from '../../hooks/useStore'; import { useMarkerSymbol } from './MarkerSymbols'; import type { ReactFlowState } from '../../types'; -type MarkerProps = EdgeMarker & { - id: string; -}; - type MarkerDefinitionsProps = { defaultColor: string; rfId?: string; @@ -51,22 +47,9 @@ const Marker = ({ const markerSelector = ({ defaultColor, rfId }: { defaultColor: string; rfId?: string }) => (s: ReactFlowState) => { - const ids: string[] = []; + const markers = createMarkerIds(s.edges, { id: rfId, defaultColor }); - return s.edges - .reduce((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)); + return markers; }; // when you have multiple flows on a page and you hide the first one, the other ones have no markers anymore diff --git a/packages/svelte/src/lib/components/BaseEdge/BaseEdge.svelte b/packages/svelte/src/lib/components/BaseEdge/BaseEdge.svelte index 2bf450d1..c684cfc5 100644 --- a/packages/svelte/src/lib/components/BaseEdge/BaseEdge.svelte +++ b/packages/svelte/src/lib/components/BaseEdge/BaseEdge.svelte @@ -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; - + {#if interactionWidth} & { +export type BaseEdgeProps = Pick & { + id?: string; path: string; labelX?: number; labelY?: number; + markerStart?: string; + markerEnd?: string; }; diff --git a/packages/svelte/src/lib/components/EdgeWrapper/EdgeWrapper.svelte b/packages/svelte/src/lib/components/EdgeWrapper/EdgeWrapper.svelte index dae654ca..ba8a0d1b 100644 --- a/packages/svelte/src/lib/components/EdgeWrapper/EdgeWrapper.svelte +++ b/packages/svelte/src/lib/components/EdgeWrapper/EdgeWrapper.svelte @@ -1,6 +1,7 @@ - + + diff --git a/packages/svelte/src/lib/components/edges/SmoothStepEdge.svelte b/packages/svelte/src/lib/components/edges/SmoothStepEdge.svelte index b43dd789..f5093c54 100644 --- a/packages/svelte/src/lib/components/edges/SmoothStepEdge.svelte +++ b/packages/svelte/src/lib/components/edges/SmoothStepEdge.svelte @@ -16,4 +16,14 @@ }); - + diff --git a/packages/svelte/src/lib/components/edges/StepEdge.svelte b/packages/svelte/src/lib/components/edges/StepEdge.svelte index b765adcc..c51d6bcb 100644 --- a/packages/svelte/src/lib/components/edges/StepEdge.svelte +++ b/packages/svelte/src/lib/components/edges/StepEdge.svelte @@ -17,4 +17,14 @@ }); - + diff --git a/packages/svelte/src/lib/components/edges/StraightEdge.svelte b/packages/svelte/src/lib/components/edges/StraightEdge.svelte index a0123b67..8b095be9 100644 --- a/packages/svelte/src/lib/components/edges/StraightEdge.svelte +++ b/packages/svelte/src/lib/components/edges/StraightEdge.svelte @@ -14,4 +14,15 @@ }); - + + diff --git a/packages/svelte/src/lib/container/EdgeRenderer/EdgeRenderer.svelte b/packages/svelte/src/lib/container/EdgeRenderer/EdgeRenderer.svelte index a60efbb8..6c495dd9 100644 --- a/packages/svelte/src/lib/container/EdgeRenderer/EdgeRenderer.svelte +++ b/packages/svelte/src/lib/container/EdgeRenderer/EdgeRenderer.svelte @@ -1,5 +1,6 @@ + + + {#if type === MarkerType.Arrow} + + {:else if type === MarkerType.ArrowClosed} + + {/if} + \ No newline at end of file diff --git a/packages/svelte/src/lib/container/EdgeRenderer/MarkerDefinition/MarkerDefinition.svelte b/packages/svelte/src/lib/container/EdgeRenderer/MarkerDefinition/MarkerDefinition.svelte new file mode 100644 index 00000000..602d6b57 --- /dev/null +++ b/packages/svelte/src/lib/container/EdgeRenderer/MarkerDefinition/MarkerDefinition.svelte @@ -0,0 +1,21 @@ + + + + {#each $markers as marker(marker.id)} + + {/each} + \ No newline at end of file diff --git a/packages/svelte/src/lib/container/EdgeRenderer/MarkerDefinition/index.ts b/packages/svelte/src/lib/container/EdgeRenderer/MarkerDefinition/index.ts new file mode 100644 index 00000000..9e2cc390 --- /dev/null +++ b/packages/svelte/src/lib/container/EdgeRenderer/MarkerDefinition/index.ts @@ -0,0 +1,2 @@ +export { default as MarkerDefinition } from './MarkerDefinition.svelte'; +export { default as Marker } from './Marker.svelte'; diff --git a/packages/svelte/src/lib/container/SvelteFlow/SvelteFlow.svelte b/packages/svelte/src/lib/container/SvelteFlow/SvelteFlow.svelte index e1cc77a9..35eb0247 100644 --- a/packages/svelte/src/lib/container/SvelteFlow/SvelteFlow.svelte +++ b/packages/svelte/src/lib/container/SvelteFlow/SvelteFlow.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 => { diff --git a/packages/svelte/src/lib/container/SvelteFlow/types.ts b/packages/svelte/src/lib/container/SvelteFlow/types.ts index 6526bd5d..539de845 100644 --- a/packages/svelte/src/lib/container/SvelteFlow/types.ts +++ b/packages/svelte/src/lib/container/SvelteFlow/types.ts @@ -32,6 +32,7 @@ export type SvelteFlowProps = DOMAttributes & { connectionRadius?: number; selectionMode?: SelectionMode; snapGrid?: SnapGrid; + defaultMarkerColor?: string; class?: string; style?: string; diff --git a/packages/svelte/src/lib/plugins/Background/Background.svelte b/packages/svelte/src/lib/plugins/Background/Background.svelte index 908d3f6d..4ef0c66b 100644 --- a/packages/svelte/src/lib/plugins/Background/Background.svelte +++ b/packages/svelte/src/lib/plugins/Background/Background.svelte @@ -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]; diff --git a/packages/svelte/src/lib/store/index.ts b/packages/svelte/src/lib/store/index.ts index 273b6ffc..3693a518 100644 --- a/packages/svelte/src/lib/store/index.ts +++ b/packages/svelte/src/lib/store/index.ts @@ -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, diff --git a/packages/svelte/src/lib/store/initial-store.ts b/packages/svelte/src/lib/store/initial-store.ts index 31e37833..a11d2a29 100644 --- a/packages/svelte/src/lib/store/initial-store.ts +++ b/packages/svelte/src/lib/store/initial-store.ts @@ -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(null), + flowId: writable(null), nodes: writable([]), edges: writable([]), edgesLayouted: readable([]), @@ -80,5 +81,7 @@ export const initialStoreState = { connection: writable(initConnectionData), connectionRadius: writable(25), connectionLineType: writable(ConnectionLineType.Bezier), - isValidConnection: writable(() => true) + isValidConnection: writable(() => true), + markers: readable([]), + defaultMarkerColor: writable('#b1b1b7') }; diff --git a/packages/svelte/src/lib/types/edges.ts b/packages/svelte/src/lib/types/edges.ts index a60f5f76..24f8af72 100644 --- a/packages/svelte/src/lib/types/edges.ts +++ b/packages/svelte/src/lib/types/edges.ts @@ -47,11 +47,15 @@ export type EdgeLayouted = Omit & { 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>; diff --git a/packages/system/src/types/edges.ts b/packages/system/src/types/edges.ts index 6698ca52..3b9e4e29 100644 --- a/packages/system/src/types/edges.ts +++ b/packages/system/src/types/edges.ts @@ -57,3 +57,7 @@ export enum MarkerType { Arrow = 'arrow', ArrowClosed = 'arrowclosed', } + +export type MarkerProps = EdgeMarker & { + id: string; +}; diff --git a/packages/utils/src/graph.ts b/packages/utils/src/graph.ts index c8f608e6..9f2bc2c7 100644 --- a/packages/utils/src/graph.ts +++ b/packages/utils/src/graph.ts @@ -5,7 +5,6 @@ import { boxToRect, clamp, devWarn, getBoundsOfBoxes, getOverlappingArea, rectTo import { errorMessages, type Connection, - type EdgeMarkerType, type Transform, type XYPosition, type Rect, @@ -54,23 +53,6 @@ export const getIncomersBase = `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[]) => { return edges.some( (el) => diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index 60a93bf6..d4a84150 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -1,2 +1,3 @@ export * from './graph'; export * from './utils'; +export * from './marker'; diff --git a/packages/utils/src/marker.ts b/packages/utils/src/marker.ts new file mode 100644 index 00000000..cc13bf48 --- /dev/null +++ b/packages/utils/src/marker.ts @@ -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((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)); +}