refactor(svelte): use custom stores for nodes and edges
This commit is contained in:
@@ -87,7 +87,6 @@ export default function drag(
|
||||
const dragHandler = d3Drag()
|
||||
.on('start', (event: UseDragEvent) => {
|
||||
const pointerPos = getPointerPosition(event);
|
||||
console.log(pointerPos);
|
||||
lastPos = pointerPos;
|
||||
dragItems = getDragItems(get(nodes), pointerPos, nodeId);
|
||||
})
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { setContext, onMount, createEventDispatcher } from 'svelte';
|
||||
import { setContext, onMount } from 'svelte';
|
||||
import cc from 'classcat';
|
||||
|
||||
import { key, createStore } from '$lib/store';
|
||||
@@ -18,8 +18,8 @@
|
||||
type $$Events = SvelteFlowEvents;
|
||||
|
||||
export let id: $$Props['id'] = '1';
|
||||
export let nodes: $$Props['nodes'] = [];
|
||||
export let edges: $$Props['edges'] = [];
|
||||
export let nodes: $$Props['nodes'];
|
||||
export let edges: $$Props['edges'];
|
||||
export let fitView: $$Props['fitView'] = undefined;
|
||||
export let minZoom: $$Props['minZoom'] = undefined;
|
||||
export let maxZoom: $$Props['maxZoom'] = undefined;
|
||||
@@ -28,7 +28,6 @@
|
||||
export let edgeTypes: $$Props['edgeTypes'] = undefined;
|
||||
export let selectionKey: $$Props['selectionKey'] = undefined;
|
||||
export let deleteKey: $$Props['deleteKey'] = undefined;
|
||||
export let defaultEdgeOptions: $$Props['defaultEdgeOptions'] = undefined;
|
||||
export let connectionRadius: $$Props['connectionRadius'] = undefined;
|
||||
export let connectionLineType: $$Props['connectionLineType'] = undefined
|
||||
export let style: $$Props['style'] = undefined;
|
||||
@@ -38,6 +37,8 @@
|
||||
let domNode: HTMLDivElement;
|
||||
|
||||
const store = createStore({
|
||||
nodes,
|
||||
edges,
|
||||
fitView,
|
||||
nodeTypes
|
||||
});
|
||||
@@ -51,15 +52,6 @@
|
||||
store.width.set(width);
|
||||
store.height.set(height);
|
||||
store.domNode.set(domNode);
|
||||
|
||||
// @todo: is this a svelte way for two way binding?
|
||||
store.nodes.subscribe((ns) => {
|
||||
nodes = ns;
|
||||
});
|
||||
|
||||
store.edges.subscribe((es) => {
|
||||
edges = es;
|
||||
});
|
||||
});
|
||||
|
||||
$: {
|
||||
@@ -67,7 +59,6 @@
|
||||
id,
|
||||
connectionLineType,
|
||||
connectionRadius,
|
||||
defaultEdgeOptions
|
||||
};
|
||||
|
||||
Object.keys(updatableProps).forEach(prop => {
|
||||
@@ -79,14 +70,6 @@
|
||||
})
|
||||
}
|
||||
|
||||
$: {
|
||||
store.setNodes(nodes);
|
||||
}
|
||||
|
||||
$: {
|
||||
store.setEdges(edges);
|
||||
}
|
||||
|
||||
$: {
|
||||
if (nodeTypes !== undefined) {
|
||||
store.setNodeTypes(nodeTypes);
|
||||
|
||||
@@ -14,10 +14,12 @@ import type {
|
||||
EdgeTypes,
|
||||
DefaultEdgeOptions
|
||||
} from '$lib/types';
|
||||
import type { Writable } from 'svelte/store';
|
||||
import type { createNodes } from '$lib/utils';
|
||||
|
||||
export type SvelteFlowProps = {
|
||||
nodes: Node[];
|
||||
edges: Edge[];
|
||||
nodes: ReturnType<typeof createNodes>;
|
||||
edges: Writable<Edge[]>;
|
||||
|
||||
id?: string;
|
||||
nodeTypes?: NodeTypes;
|
||||
|
||||
@@ -30,10 +30,13 @@ import {
|
||||
initialStoreState
|
||||
} from './initial-store';
|
||||
import type { SvelteFlowStore } from './types';
|
||||
import type { SvelteFlowProps } from '$lib/container/SvelteFlow/types';
|
||||
|
||||
export const key = Symbol();
|
||||
|
||||
type CreateStoreProps = {
|
||||
nodes: SvelteFlowProps['nodes'];
|
||||
edges: SvelteFlowProps['edges'];
|
||||
fitView?: boolean;
|
||||
nodeOrigin?: NodeOrigin;
|
||||
transform?: Transform;
|
||||
@@ -42,9 +45,15 @@ type CreateStoreProps = {
|
||||
id?: string;
|
||||
};
|
||||
|
||||
export function createStore({ fitView: fitViewOnInit = false }: CreateStoreProps): SvelteFlowStore {
|
||||
export function createStore({
|
||||
fitView: fitViewOnInit = false,
|
||||
nodes,
|
||||
edges
|
||||
}: CreateStoreProps): SvelteFlowStore {
|
||||
const store = {
|
||||
...initialStoreState
|
||||
...initialStoreState,
|
||||
nodes,
|
||||
edges
|
||||
};
|
||||
|
||||
let fitViewOnInitDone = false;
|
||||
@@ -63,36 +72,9 @@ export function createStore({ fitView: fitViewOnInit = false }: CreateStoreProps
|
||||
});
|
||||
}
|
||||
|
||||
function setEdges(edges: Edge[]) {
|
||||
const defaultEdgeOptions = get(store.defaultEdgeOptions);
|
||||
const nextEdges = defaultEdgeOptions
|
||||
? edges.map((e) => ({ ...defaultEdgeOptions, ...e }))
|
||||
: edges;
|
||||
store.edges.set(nextEdges);
|
||||
}
|
||||
|
||||
function addEdge(edgeParams: Edge | Connection) {
|
||||
const edges = get(store.edges);
|
||||
setEdges(addEdgeUtil(edgeParams, edges));
|
||||
}
|
||||
|
||||
function setNodes(nodes: Node[]) {
|
||||
const defaultNodeOptions = get(store.defaultNodeOptions) || {};
|
||||
|
||||
store.nodes.update((currentNodes) => {
|
||||
const nextNodes = nodes.map((n) => {
|
||||
const currentNode = currentNodes.find((cn) => cn.id === n.id) || {};
|
||||
|
||||
return {
|
||||
...defaultNodeOptions,
|
||||
...currentNode,
|
||||
...n,
|
||||
positionAbsolute: n.position
|
||||
};
|
||||
});
|
||||
|
||||
return nextNodes;
|
||||
});
|
||||
store.edges.set(addEdgeUtil(edgeParams, edges));
|
||||
}
|
||||
|
||||
function updateNodePositions(nodeDragItems: NodeDragItem[], dragging = false) {
|
||||
@@ -332,8 +314,6 @@ export function createStore({ fitView: fitViewOnInit = false }: CreateStoreProps
|
||||
connectionPath: getConnectionPath(store),
|
||||
|
||||
// actions
|
||||
setNodes,
|
||||
setEdges,
|
||||
setNodeTypes,
|
||||
setEdgeTypes,
|
||||
addEdge,
|
||||
|
||||
@@ -16,16 +16,7 @@ import OutputNode from '$lib/components/nodes/OutputNode.svelte';
|
||||
import BezierEdge from '$lib/components/edges/BezierEdge.svelte';
|
||||
import StraightEdge from '$lib/components/edges/StraightEdge.svelte';
|
||||
import SmoothStepEdge from '$lib/components/edges/SmoothStepEdge.svelte';
|
||||
import type {
|
||||
Node,
|
||||
Edge,
|
||||
ConnectionData,
|
||||
NodeTypes,
|
||||
EdgeTypes,
|
||||
EdgeLayouted,
|
||||
DefaultEdgeOptions,
|
||||
DefaultNodeOptions
|
||||
} from '$lib/types';
|
||||
import type { ConnectionData, NodeTypes, EdgeTypes, EdgeLayouted } from '$lib/types';
|
||||
|
||||
export const initConnectionData = {
|
||||
nodeId: null,
|
||||
@@ -49,8 +40,6 @@ export const initialEdgeTypes = {
|
||||
|
||||
export const initialStoreState = {
|
||||
id: writable<string | null>(null),
|
||||
nodes: writable<Node[]>([]),
|
||||
edges: writable<Edge[]>([]),
|
||||
edgesLayouted: readable<EdgeLayouted[]>([]),
|
||||
height: writable<number>(500),
|
||||
width: writable<number>(500),
|
||||
@@ -61,8 +50,6 @@ export const initialStoreState = {
|
||||
zoom: null,
|
||||
selection: null
|
||||
}),
|
||||
defaultNodeOptions: writable<DefaultNodeOptions | null>(null),
|
||||
defaultEdgeOptions: writable<DefaultEdgeOptions | null>(null),
|
||||
dragging: writable<boolean>(false),
|
||||
selectionRect: writable<SelectionRect | null>(null),
|
||||
selectionKeyPressed: writable<boolean>(false),
|
||||
|
||||
@@ -8,10 +8,9 @@ import type {
|
||||
|
||||
import type { initialStoreState } from './initial-store';
|
||||
import type { Node, Edge, ConnectionData, NodeTypes, EdgeTypes } from '$lib/types';
|
||||
import type { Writable } from 'svelte/store';
|
||||
|
||||
export type SvelteFlowStoreActions = {
|
||||
setNodes: (nodes: Node[]) => void;
|
||||
setEdges: (edges: Edge[]) => void;
|
||||
setNodeTypes: (nodeTypes: NodeTypes) => void;
|
||||
setEdgeTypes: (edgeTypes: EdgeTypes) => void;
|
||||
addEdge: (edge: Edge | Connection) => void;
|
||||
@@ -33,6 +32,9 @@ export type SvelteFlowStoreActions = {
|
||||
cancelConnection: () => void;
|
||||
};
|
||||
|
||||
export type SvelteFlowStoreState = typeof initialStoreState;
|
||||
export type SvelteFlowStoreState = typeof initialStoreState & {
|
||||
nodes: Writable<Node[]>;
|
||||
edges: Writable<Edge[]>;
|
||||
};
|
||||
|
||||
export type SvelteFlowStore = SvelteFlowStoreState & SvelteFlowStoreActions;
|
||||
|
||||
@@ -9,7 +9,8 @@ import {
|
||||
getConnectedEdgesBase,
|
||||
getDimensions
|
||||
} from '@reactflow/utils';
|
||||
import type { Edge, Node } from '$lib/types';
|
||||
import type { DefaultEdgeOptions, DefaultNodeOptions, Edge, Node } from '$lib/types';
|
||||
import { writable, type Writable } from 'svelte/store';
|
||||
|
||||
export const isNode = isNodeBase<Node, Edge>;
|
||||
export const isEdge = isEdgeBase<Node, Edge>;
|
||||
@@ -45,3 +46,67 @@ export const getHandleBounds = (
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
export const createNodes = (
|
||||
nodes: Node[],
|
||||
defaultOptions?: DefaultNodeOptions
|
||||
): Writable<Node[]> & { setDefaultOptions: (opts: DefaultNodeOptions) => void } => {
|
||||
const { subscribe, set, update } = writable<Node[]>([]);
|
||||
let value = nodes;
|
||||
let defaults = defaultOptions || {};
|
||||
|
||||
const _set: typeof set = (nds: Node[]) => {
|
||||
const nextNodes = defaults ? nds.map((node) => ({ ...defaults, ...node })) : nds;
|
||||
// @todo calculate absolute position based on parent / child relation
|
||||
const nextNodesLayouted = nextNodes.map((n) => ({ ...n, positionAbsolute: n.position }));
|
||||
|
||||
value = nextNodesLayouted;
|
||||
|
||||
set(value);
|
||||
};
|
||||
|
||||
const _update: typeof update = (fn: (nds: Node[]) => Node[]) => _set(fn(value));
|
||||
|
||||
const setDefaultOptions = (options: DefaultNodeOptions) => {
|
||||
defaults = options;
|
||||
};
|
||||
|
||||
_set(value);
|
||||
|
||||
return {
|
||||
subscribe,
|
||||
set: _set,
|
||||
update: _update,
|
||||
setDefaultOptions
|
||||
};
|
||||
};
|
||||
|
||||
export const createEdges = (
|
||||
edges: Edge[],
|
||||
defaultOptions?: DefaultEdgeOptions
|
||||
): Writable<Edge[]> & { setDefaultOptions: (opts: DefaultEdgeOptions) => void } => {
|
||||
const { subscribe, set, update } = writable<Edge[]>([]);
|
||||
let value = edges;
|
||||
let defaults = defaultOptions || {};
|
||||
|
||||
const _set: typeof set = (eds: Edge[]) => {
|
||||
const nextEdges = defaults ? eds.map((edge) => ({ ...defaults, ...edge })) : eds;
|
||||
value = nextEdges;
|
||||
set(value);
|
||||
};
|
||||
|
||||
const _update: typeof update = (fn: (eds: Edge[]) => Edge[]) => _set(fn(value));
|
||||
|
||||
const setDefaultOptions = (options: DefaultEdgeOptions) => {
|
||||
defaults = options;
|
||||
};
|
||||
|
||||
_set(value);
|
||||
|
||||
return {
|
||||
subscribe,
|
||||
set: _set,
|
||||
update: _update,
|
||||
setDefaultOptions
|
||||
};
|
||||
};
|
||||
|
||||
@@ -5,7 +5,9 @@
|
||||
BackgroundVariant,
|
||||
Minimap,
|
||||
Panel,
|
||||
type Node, type Edge, type NodeTypes
|
||||
createNodes,
|
||||
createEdges,
|
||||
type NodeTypes
|
||||
} from '../lib/index';
|
||||
import CustomNode from '../customnodes/Custom.svelte';
|
||||
|
||||
@@ -47,7 +49,7 @@
|
||||
// }
|
||||
// }
|
||||
|
||||
let nodes: Node<{ label: string }>[] = [
|
||||
let nodes = createNodes([
|
||||
{
|
||||
id: '1',
|
||||
type: 'input',
|
||||
@@ -93,9 +95,9 @@
|
||||
data: { label: 'Custom Node' },
|
||||
position: { x: 150, y: 300 }
|
||||
}
|
||||
];
|
||||
]);
|
||||
|
||||
let edges: Edge[] = [
|
||||
let edges = createEdges([
|
||||
{
|
||||
id: '1-2',
|
||||
type: 'default',
|
||||
@@ -114,28 +116,35 @@
|
||||
type: 'default',
|
||||
source: '2',
|
||||
target: '4',
|
||||
animated: true
|
||||
}
|
||||
];
|
||||
], { animated: true });
|
||||
|
||||
function updateNode() {
|
||||
nodes[0].position = { x: nodes[0].position.x + 20, y: nodes[0].position.y };
|
||||
nodes.update(nds => nds.map(n => {
|
||||
if (n.id === '1') {
|
||||
return {
|
||||
...n,
|
||||
position: { x: n.position.x + 20, y: n.position.y }
|
||||
}
|
||||
}
|
||||
|
||||
return n;
|
||||
}));
|
||||
}
|
||||
|
||||
// $: {
|
||||
// console.log('nodes changed', nodes)
|
||||
// }
|
||||
$: {
|
||||
console.log('nodes changed', $nodes)
|
||||
}
|
||||
</script>
|
||||
|
||||
<SvelteFlow
|
||||
bind:nodes
|
||||
bind:edges
|
||||
{nodes}
|
||||
{edges}
|
||||
{nodeTypes}
|
||||
fitView
|
||||
minZoom={0.1}
|
||||
maxZoom={2.5}
|
||||
initialViewport={{ x: 100, y: 100, zoom: 2 }}
|
||||
defaultEdgeOptions={{ animated: true }}
|
||||
on:node:click={(event) => console.log('on node click', event)}
|
||||
on:node:mouseenter={(event) => console.log('on node enter', event)}
|
||||
on:node:mouseleave={(event) => console.log('on node leave', event)}
|
||||
|
||||
Reference in New Issue
Block a user