feat(svelte): edge types, use edge-utils

This commit is contained in:
moklick
2023-02-22 10:38:32 +01:00
parent b3de45ce1a
commit a0bdd9574e
15 changed files with 219 additions and 82 deletions
+1
View File
@@ -16,6 +16,7 @@
".": "./index.js" ".": "./index.js"
}, },
"dependencies": { "dependencies": {
"@reactflow/edge-utils": "workspace:*",
"@reactflow/system": "workspace:*", "@reactflow/system": "workspace:*",
"@reactflow/utils": "workspace:*", "@reactflow/utils": "workspace:*",
"@svelte-put/shortcut": "^2.0.0", "@svelte-put/shortcut": "^2.0.0",
@@ -10,8 +10,8 @@
<div class="custom"> <div class="custom">
<div>{data.label}</div> <div>{data.label}</div>
<div>{~~xPos}, {~~yPos}</div> <div>{~~xPos}, {~~yPos}</div>
<Handle type="target" position={Position.Left} /> <Handle type="target" position={Position.Top} />
<Handle type="source" position={Position.Right} /> <Handle type="source" position={Position.Bottom} />
</div> </div>
<style> <style>
@@ -1,26 +1,44 @@
<script lang="ts"> <script lang="ts">
export let id: string = '1'; import type { SvelteComponentTyped } from 'svelte';
import { Position } from '@reactflow/system';
import { useStore } from '$lib/store';
import BezierEdge from '$lib/components/edges/StraightEdge.svelte';
import type { EdgeProps } from '$lib/types';
export let id: string;
export let type: string = 'default'; export let type: string = 'default';
export let sourceX: number = 0; export let sourceX: number = 0;
export let sourceY: number = 0; export let sourceY: number = 0;
export let targetX: number = 0; export let targetX: number = 0;
export let targetY: number = 0; export let targetY: number = 0;
export let sourcePosition: Position = Position.Bottom;
export let targetPosition: Position = Position.Top;
const { edgeTypesStore } = useStore();
const edgeComponent: typeof SvelteComponentTyped<EdgeProps> = $edgeTypesStore[type] || BezierEdge;
</script> </script>
<g <g
class="react-flow__edge" class="react-flow__edge"
data-id={id} data-id={id}
> >
<path <svelte:component
d={`M ${sourceX},${sourceY}L ${targetX},${targetY}`} this={edgeComponent}
fill="none" {id}
class="react-flow__edge-path" {sourceX}
{sourceY}
{targetX}
{targetY}
{sourcePosition}
{targetPosition}
/> />
</g> </g>
<style> <style>
.react-flow__edge-path { .react-flow__edge :global(path) {
stroke: #ccc; stroke: #ccc;
stroke-width: 1; stroke-width: 1;
fill: none;
} }
</style> </style>
@@ -0,0 +1,21 @@
<script lang="ts">
import { getBezierPath } from '@reactflow/edge-utils';
import type { EdgeProps } from '$lib/types';
interface $$Props extends EdgeProps {}
export let id: $$Props['id'];
export let sourceX: $$Props['sourceX'] = 0;
export let sourceY: $$Props['sourceY'] = 0;
export let targetX: $$Props['targetX'] = 0;
export let targetY: $$Props['targetY'] = 0;
export let sourcePosition: $$Props['sourcePosition'] = undefined;
export let targetPosition: $$Props['targetPosition'] = undefined;
$: [path] = getBezierPath({ sourceX, sourceY, targetX, targetY, sourcePosition, targetPosition });
</script>
<path
d={path}
class="react-flow__edge-path"
/>
@@ -0,0 +1,21 @@
<script lang="ts">
import { getSmoothStepPath } from '@reactflow/edge-utils';
import type { EdgeProps } from '$lib/types';
interface $$Props extends EdgeProps {}
export let id: $$Props['id'];
export let sourceX: $$Props['sourceX'] = 0;
export let sourceY: $$Props['sourceY'] = 0;
export let targetX: $$Props['targetX'] = 0;
export let targetY: $$Props['targetY'] = 0;
export let sourcePosition: $$Props['sourcePosition'] = undefined;
export let targetPosition: $$Props['targetPosition'] = undefined;
$: [path] = getSmoothStepPath({ sourceX, sourceY, targetX, targetY, sourcePosition, targetPosition });
</script>
<path
d={path}
class="react-flow__edge-path"
/>
@@ -0,0 +1,19 @@
<script lang="ts">
import { getStraightPath } from '@reactflow/edge-utils';
import type { EdgeProps } from '$lib/types';
interface $$Props extends EdgeProps {}
export let id: $$Props['id'];
export let sourceX: $$Props['sourceX'] = 0;
export let sourceY: $$Props['sourceY'] = 0;
export let targetX: $$Props['targetX'] = 0;
export let targetY: $$Props['targetY'] = 0;
$: [path] = getStraightPath({ sourceX, sourceY, targetX, targetY });
</script>
<path
d={path}
class="react-flow__edge-path"
/>
@@ -1,14 +1,8 @@
<script lang="ts"> <script lang="ts" context="module">
import { useStore } from '$lib/store'; export function wrapHandler(
import { SelectionMode, type Node, type Edge } from '@reactflow/system';
import { getConnectedEdges, getEventPosition, getNodesInside } from '@reactflow/utils';
const { nodesStore, edgesStore, transformStore, nodeOriginStore, draggingStore, selectionRectStore, selectionRectModeStore, selectionKeyPressedStore, resetSelectedElements } = useStore();
const wrapHandler = (
handler: (evt: MouseEvent) => void, handler: (evt: MouseEvent) => void,
container: HTMLDivElement container: HTMLDivElement
): (evt: MouseEvent) => void => { ): (evt: MouseEvent) => void {
return (event: MouseEvent) => { return (event: MouseEvent) => {
if (event.target !== container) { if (event.target !== container) {
return; return;
@@ -18,6 +12,29 @@
}; };
}; };
export function toggleSelected<Item extends Node | Edge>(ids: string[]) {
return (item: Item) => {
const isSelected = ids.includes(item.id);
if (item.selected !== isSelected) {
return {
...item,
selected: isSelected,
};
}
return item;
}
}
</script>
<script lang="ts">
import { useStore } from '$lib/store';
import { SelectionMode, type Node, type Edge } from '@reactflow/system';
import { getConnectedEdges, getEventPosition, getNodesInside } from '@reactflow/utils';
const { nodesStore, edgesStore, transformStore, nodeOriginStore, draggingStore, selectionRectStore, selectionRectModeStore, selectionKeyPressedStore, resetSelectedElements } = useStore();
// @todo take from props // @todo take from props
const elementsSelectable = true; const elementsSelectable = true;
const selectionMode = SelectionMode.Partial const selectionMode = SelectionMode.Partial
@@ -67,20 +84,7 @@
// onSelectionStart?.(event); // onSelectionStart?.(event);
}; };
function toggleSelected<Item extends Node | Edge>(ids: string[]) {
return (item: Item) => {
const isSelected = ids.includes(item.id);
if (item.selected !== isSelected) {
return {
...item,
selected: isSelected,
};
}
return item;
}
}
function onMouseMove(event: MouseEvent) { function onMouseMove(event: MouseEvent) {
if (!isSelecting || !containerBounds || !$selectionRectStore) { if (!isSelecting || !containerBounds || !$selectionRectStore) {
@@ -4,9 +4,9 @@
export let position: PanelPosition = 'top-right'; export let position: PanelPosition = 'top-right';
export let style: string = ''; export let style: string = '';
export { className as class }
let className: string; let className: string;
export { className as class }
$: positionClasses = `${position}`.split('-'); $: positionClasses = `${position}`.split('-');
</script> </script>
+16 -1
View File
@@ -27,7 +27,10 @@ import { SelectionMode } from 'reactflow';
import DefaultNode from '$lib/components/nodes/DefaultNode.svelte'; import DefaultNode from '$lib/components/nodes/DefaultNode.svelte';
import InputNode from '$lib/components/nodes/InputNode.svelte'; import InputNode from '$lib/components/nodes/InputNode.svelte';
import OutputNode from '$lib/components/nodes/OutputNode.svelte'; import OutputNode from '$lib/components/nodes/OutputNode.svelte';
import type { NodeTypes } from '$lib/types'; import type { EdgeTypes, NodeTypes } from '$lib/types';
import BezierEdge from '$lib/components/edges/BezierEdge.svelte';
import StraightEdge from '$lib/components/edges/StraightEdge.svelte';
import SmoothStepEdge from '$lib/components/edges/SmoothStepEdge.svelte';
export const key = Symbol(); export const key = Symbol();
@@ -38,6 +41,7 @@ type CreateStoreProps = {
nodeOrigin?: NodeOrigin; nodeOrigin?: NodeOrigin;
transform?: Transform; transform?: Transform;
nodeTypes?: NodeTypes; nodeTypes?: NodeTypes;
edgeTypes?: EdgeTypes;
id?: string; id?: string;
}; };
@@ -65,6 +69,7 @@ type SvelteFlowStore = {
selectionMode: Writable<SelectionMode>; selectionMode: Writable<SelectionMode>;
selectionKeyPressedStore: Writable<boolean>; selectionKeyPressedStore: Writable<boolean>;
nodeTypesStore: Writable<NodeTypes>; nodeTypesStore: Writable<NodeTypes>;
edgeTypesStore: Writable<EdgeTypes>;
zoomIn: (options?: ViewportHelperFunctionOptions) => void; zoomIn: (options?: ViewportHelperFunctionOptions) => void;
zoomOut: (options?: ViewportHelperFunctionOptions) => void; zoomOut: (options?: ViewportHelperFunctionOptions) => void;
fitView: (options?: ViewportHelperFunctionOptions) => boolean; fitView: (options?: ViewportHelperFunctionOptions) => boolean;
@@ -84,6 +89,7 @@ export function createStore({
nodeOrigin = [0, 0], nodeOrigin = [0, 0],
fitView: fitViewOnInit = false, fitView: fitViewOnInit = false,
nodeTypes = {}, nodeTypes = {},
edgeTypes = {},
id = '1' id = '1'
}: CreateStoreProps): SvelteFlowStore { }: CreateStoreProps): SvelteFlowStore {
const nodesStore = writable(nodes.map((n) => ({ ...n, positionAbsolute: n.position }))); const nodesStore = writable(nodes.map((n) => ({ ...n, positionAbsolute: n.position })));
@@ -108,6 +114,13 @@ export function createStore({
default: nodeTypes.default || DefaultNode default: nodeTypes.default || DefaultNode
}); });
const edgeTypesStore = writable({
...edgeTypes,
straight: edgeTypes.straight || StraightEdge,
smoothstep: edgeTypes.smoothstep || SmoothStepEdge,
default: edgeTypes.default || BezierEdge
});
let fitViewOnInitDone = false; let fitViewOnInitDone = false;
const edgesWithDataStore = derived([edgesStore, nodesStore], ([$edges, $nodes]) => { const edgesWithDataStore = derived([edgesStore, nodesStore], ([$edges, $nodes]) => {
@@ -293,6 +306,7 @@ export function createStore({
selectionRectModeStore, selectionRectModeStore,
selectionMode, selectionMode,
nodeTypesStore, nodeTypesStore,
edgeTypesStore,
updateNodePositions, updateNodePositions,
updateNodeDimensions, updateNodeDimensions,
zoomIn, zoomIn,
@@ -304,5 +318,6 @@ export function createStore({
export function useStore(): SvelteFlowStore { export function useStore(): SvelteFlowStore {
const { getStore } = getContext<{ getStore: () => SvelteFlowStore }>(key); const { getStore } = getContext<{ getStore: () => SvelteFlowStore }>(key);
return getStore(); return getStore();
} }
+12
View File
@@ -13,3 +13,15 @@ export type NodeProps<NodeData extends Record<string, unknown> = Record<string,
}; };
export type NodeTypes = Record<string, typeof SvelteComponentTyped<NodeProps>>; export type NodeTypes = Record<string, typeof SvelteComponentTyped<NodeProps>>;
export type EdgeProps = {
id: string;
sourceX: number;
sourceY: number;
targetX: number;
targetY: number;
sourcePosition?: Position;
targetPosition?: Position;
};
export type EdgeTypes = Record<string, typeof SvelteComponentTyped<EdgeProps>>;
+69 -49
View File
@@ -4,64 +4,84 @@
import SvelteFlow, { Controls, Background, BackgroundVariant, Minimap } from '../lib/index'; import SvelteFlow, { Controls, Background, BackgroundVariant, Minimap } from '../lib/index';
import CustomNode from '../customnodes/Custom.svelte'; import CustomNode from '../customnodes/Custom.svelte';
const yNodes = 10;
const xNodes = 10;
const nodes: Node[] = [];
const edges: Edge[] = [];
const nodeTypes = { const nodeTypes = {
custom: CustomNode, custom: CustomNode,
}; };
let source = null; // const yNodes = 10;
// const xNodes = 10;
for (let y = 0; y < yNodes; y++) { // const nodes: Node[] = [];
for (let x = 0; x < xNodes; x++) { // const edges: Edge[] = [];
const position = { x: x * 100, y: y * 50 };
const id = `${x}-${y}`;
const data = { label: `Node ${id}` };
const node = {
id,
data,
position,
type: x === 0 ? 'custom' : 'default',
};
nodes.push(node);
if (source) {
const edge = {
id: `${source.id}-${id}`,
source: source.id,
target: id,
};
edges.push(edge);
}
source = node;
}
}
// const nodes = [{ // let source = null;
// id: '1',
// type: 'input',
// data: { label: 'Input Node' },
// position: { x: 250, y: 5 }
// }, {
// id: '2',
// type: 'input',
// data: { label: 'Input Node 2' },
// position: { x: 150, y: 250 },
// }]
// const edges = [{ // for (let y = 0; y < yNodes; y++) {
// id: '1-2', // for (let x = 0; x < xNodes; x++) {
// type: 'default', // const position = { x: x * 100, y: y * 50 };
// source: '1', // const id = `${x}-${y}`;
// target: '2' // const data = { label: `Node ${id}` };
// }] // const node = {
// id,
// data,
// position,
// type: x === 0 ? 'custom' : 'default',
// };
// nodes.push(node);
// if (source) {
// const edge = {
// id: `${source.id}-${id}`,
// source: source.id,
// target: id,
// };
// edges.push(edge);
// }
// source = node;
// }
// }
const nodes: Node[] = [{
id: '1',
type: 'input',
data: { label: 'Input Node' },
position: { x: 150, y: 5 }
}, {
id: '2',
type: 'default',
data: { label: 'Node' },
position: { x: 0, y: 150 },
}, {
id: '3',
type: 'output',
data: { label: 'Output Node' },
position: { x: 300, y: 150 },
}, {
id: '4',
type: 'custom',
data: { label: 'Custom Node' },
position: { x: 150, y: 300 },
}];
const edges: Edge[] = [{
id: '1-2',
type: 'default',
source: '1',
target: '2'
}, {
id: '1-3',
type: 'smoothstep',
source: '1',
target: '3'
}, {
id: '2-4',
type: 'default',
source: '2',
target: '4'
}];
</script> </script>
<SvelteFlow {nodes} {edges} {nodeTypes} fitView> <SvelteFlow {nodes} {edges} {nodeTypes} fitView>
+1 -1
View File
@@ -57,6 +57,6 @@
}, },
"rollup": { "rollup": {
"globals": {}, "globals": {},
"name": "ReactFlowCore" "name": "ReactFlowSystem"
} }
} }
+2
View File
@@ -77,6 +77,8 @@ export type FitViewParams = {
nodeOrigin: NodeOrigin; nodeOrigin: NodeOrigin;
d3Zoom: D3ZoomInstance; d3Zoom: D3ZoomInstance;
d3Selection: D3SelectionInstance; d3Selection: D3SelectionInstance;
minZoom: number;
maxZoom: number;
}; };
export type FitViewOptions = { export type FitViewOptions = {
+1 -1
View File
@@ -59,6 +59,6 @@
}, },
"rollup": { "rollup": {
"globals": {}, "globals": {},
"name": "ReactFlowCore" "name": "ReactFlowUtils"
} }
} }
+4
View File
@@ -143,6 +143,7 @@ importers:
packages/core: packages/core:
specifiers: specifiers:
'@reactflow/edge-utils': workspace:^11.5.5
'@reactflow/eslint-config': workspace:* '@reactflow/eslint-config': workspace:*
'@reactflow/rollup-config': workspace:* '@reactflow/rollup-config': workspace:*
'@reactflow/system': workspace:* '@reactflow/system': workspace:*
@@ -163,6 +164,7 @@ importers:
typescript: ^4.9.4 typescript: ^4.9.4
zustand: ^4.3.3 zustand: ^4.3.3
dependencies: dependencies:
'@reactflow/edge-utils': link:../edge-utils
'@reactflow/system': link:../system '@reactflow/system': link:../system
'@reactflow/utils': link:../utils '@reactflow/utils': link:../utils
'@types/d3': registry.npmjs.org/@types/d3/7.4.0 '@types/d3': registry.npmjs.org/@types/d3/7.4.0
@@ -310,6 +312,7 @@ importers:
packages/svelte: packages/svelte:
specifiers: specifiers:
'@reactflow/edge-utils': workspace:^11.5.5
'@reactflow/system': workspace:* '@reactflow/system': workspace:*
'@reactflow/utils': workspace:* '@reactflow/utils': workspace:*
'@svelte-put/shortcut': ^2.0.0 '@svelte-put/shortcut': ^2.0.0
@@ -335,6 +338,7 @@ importers:
typescript: ^4.9.3 typescript: ^4.9.3
vite: ^4.1.1 vite: ^4.1.1
dependencies: dependencies:
'@reactflow/edge-utils': link:../edge-utils
'@reactflow/system': link:../system '@reactflow/system': link:../system
'@reactflow/utils': link:../utils '@reactflow/utils': link:../utils
'@svelte-put/shortcut': registry.npmjs.org/@svelte-put/shortcut/2.0.0 '@svelte-put/shortcut': registry.npmjs.org/@svelte-put/shortcut/2.0.0