feat(svelte): reactive nodes
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Handle from '$lib/components/Handle/index.svelte';
|
import Handle from '$lib/components/Handle/index.svelte';
|
||||||
import { Position } from '@reactflow/system';
|
import { Position } from '@reactflow/system';
|
||||||
|
|
||||||
export let data: { label: string } = { label: 'Node' };
|
export let data: { label: string } = { label: 'Node' };
|
||||||
export let xPos: number = 0;
|
export let xPos: number = 0;
|
||||||
export let yPos: number = 0;
|
export let yPos: number = 0;
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
export let positionAbsolute: XYPosition = { x: 0, y: 0 };
|
export let positionAbsolute: XYPosition = { x: 0, y: 0 };
|
||||||
export let position: XYPosition = { x: 0, y: 0 };
|
export let position: XYPosition = { x: 0, y: 0 };
|
||||||
export let dragging: boolean = false;
|
export let dragging: boolean = false;
|
||||||
export let resizeObserver: WrapNodeProps['resizeObserver'] = undefined;
|
export let resizeObserver: WrapNodeProps['resizeObserver'] = null;
|
||||||
export let style: WrapNodeProps['style'] = undefined;
|
export let style: WrapNodeProps['style'] = undefined;
|
||||||
export let width: WrapNodeProps['width'] = undefined;
|
export let width: WrapNodeProps['width'] = undefined;
|
||||||
export let height: WrapNodeProps['height'] = undefined;
|
export let height: WrapNodeProps['height'] = undefined;
|
||||||
@@ -30,7 +30,7 @@
|
|||||||
let nodeRef: HTMLDivElement;
|
let nodeRef: HTMLDivElement;
|
||||||
|
|
||||||
const { nodesStore, transformStore, updateNodePositions, nodeTypesStore } = useStore();
|
const { nodesStore, transformStore, updateNodePositions, nodeTypesStore } = useStore();
|
||||||
const nodeComponent: typeof SvelteComponentTyped<NodeProps> = $nodeTypesStore[type] || DefaultNode;
|
const nodeComponent: typeof SvelteComponentTyped<Partial<NodeProps>> = $nodeTypesStore[type] || DefaultNode;
|
||||||
|
|
||||||
setContext('rf_nodeid', id);
|
setContext('rf_nodeid', id);
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import type { PanelPosition } from '@reactflow/system';
|
||||||
|
|
||||||
|
export type PanelProps = {
|
||||||
|
position?: PanelPosition;
|
||||||
|
style?: string;
|
||||||
|
class?: string;
|
||||||
|
};
|
||||||
@@ -1,11 +1,12 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { PanelPosition } from '@reactflow/system';
|
|
||||||
import cc from 'classcat';
|
import cc from 'classcat';
|
||||||
|
import type { PanelProps } from './Panel';
|
||||||
|
|
||||||
export let position: PanelPosition = 'top-right';
|
interface $$Props extends PanelProps {}
|
||||||
export let style: string = '';
|
|
||||||
|
|
||||||
let className: string;
|
export let position: $$Props['position'] = 'top-right';
|
||||||
|
export let style: $$Props['style'] = '';
|
||||||
|
let className: $$Props['class'] = undefined;
|
||||||
export { className as class }
|
export { className as class }
|
||||||
|
|
||||||
$: positionClasses = `${position}`.split('-');
|
$: positionClasses = `${position}`.split('-');
|
||||||
|
|||||||
@@ -11,21 +11,21 @@
|
|||||||
import UserSelection from '$lib/components/UserSelection/index.svelte';
|
import UserSelection from '$lib/components/UserSelection/index.svelte';
|
||||||
import NodeSelection from '$lib/components/NodeSelection/index.svelte';
|
import NodeSelection from '$lib/components/NodeSelection/index.svelte';
|
||||||
import KeyHandler from '$lib/components/KeyHandler/index.svelte';
|
import KeyHandler from '$lib/components/KeyHandler/index.svelte';
|
||||||
import type { NodeTypes, Node, Edge } from '$lib/types';
|
import type { SvelteFlowProps } from '$lib/types';
|
||||||
|
|
||||||
export let id: string = '1';
|
interface $$Props extends SvelteFlowProps {}
|
||||||
export let nodes: Node[] = [];
|
|
||||||
export let edges: Edge[] = [];
|
export let id: $$Props['id'] = '1';
|
||||||
export let fitView: boolean = false;
|
export let nodes: $$Props['nodes'] = [];
|
||||||
export let nodeTypes: NodeTypes;
|
export let edges: $$Props['edges'] = [];
|
||||||
let className: string = '';
|
export let fitView: $$Props['fitView'] = undefined;
|
||||||
|
export let nodeTypes: $$Props['nodeTypes'] = undefined;
|
||||||
|
let className: $$Props['class'] = undefined;
|
||||||
export { className as class };
|
export { className as class };
|
||||||
|
|
||||||
let domNode: Element;
|
let domNode: Element;
|
||||||
|
|
||||||
const store = createStore({
|
const store = createStore({
|
||||||
nodes,
|
|
||||||
edges,
|
|
||||||
fitView,
|
fitView,
|
||||||
nodeTypes
|
nodeTypes
|
||||||
});
|
});
|
||||||
@@ -38,7 +38,20 @@
|
|||||||
const { width, height } = domNode.getBoundingClientRect();
|
const { width, height } = domNode.getBoundingClientRect();
|
||||||
store.widthStore.set(width);
|
store.widthStore.set(width);
|
||||||
store.heightStore.set(height);
|
store.heightStore.set(height);
|
||||||
})
|
|
||||||
|
// @todo: is this a svelte way for two way binding?
|
||||||
|
store.nodesStore.subscribe((ns) => {
|
||||||
|
nodes = ns;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$: {
|
||||||
|
store.setNodes(nodes);
|
||||||
|
}
|
||||||
|
|
||||||
|
$: {
|
||||||
|
store.setEdges(edges);
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
|
|||||||
@@ -34,9 +34,7 @@ import SmoothStepEdge from '$lib/components/edges/SmoothStepEdge.svelte';
|
|||||||
export const key = Symbol();
|
export const key = Symbol();
|
||||||
|
|
||||||
type CreateStoreProps = {
|
type CreateStoreProps = {
|
||||||
nodes: Node[];
|
fitView?: boolean;
|
||||||
edges: Edge[];
|
|
||||||
fitView: boolean;
|
|
||||||
nodeOrigin?: NodeOrigin;
|
nodeOrigin?: NodeOrigin;
|
||||||
transform?: Transform;
|
transform?: Transform;
|
||||||
nodeTypes?: NodeTypes;
|
nodeTypes?: NodeTypes;
|
||||||
@@ -50,8 +48,8 @@ export type EdgeWithData = EdgePosition & {
|
|||||||
};
|
};
|
||||||
|
|
||||||
type SvelteFlowStore = {
|
type SvelteFlowStore = {
|
||||||
nodesStore: Writable<CreateStoreProps['nodes']>;
|
nodesStore: Writable<Node[]>;
|
||||||
edgesStore: Writable<CreateStoreProps['edges']>;
|
edgesStore: Writable<Edge[]>;
|
||||||
heightStore: Writable<number>;
|
heightStore: Writable<number>;
|
||||||
widthStore: Writable<number>;
|
widthStore: Writable<number>;
|
||||||
d3Store: Writable<{
|
d3Store: Writable<{
|
||||||
@@ -69,6 +67,8 @@ type SvelteFlowStore = {
|
|||||||
selectionKeyPressedStore: Writable<boolean>;
|
selectionKeyPressedStore: Writable<boolean>;
|
||||||
nodeTypesStore: Writable<NodeTypes>;
|
nodeTypesStore: Writable<NodeTypes>;
|
||||||
edgeTypesStore: Writable<EdgeTypes>;
|
edgeTypesStore: Writable<EdgeTypes>;
|
||||||
|
setNodes: (nodes: Node[]) => void;
|
||||||
|
setEdges: (edges: Edge[]) => void;
|
||||||
zoomIn: (options?: ViewportHelperFunctionOptions) => void;
|
zoomIn: (options?: ViewportHelperFunctionOptions) => void;
|
||||||
zoomOut: (options?: ViewportHelperFunctionOptions) => void;
|
zoomOut: (options?: ViewportHelperFunctionOptions) => void;
|
||||||
fitView: (options?: ViewportHelperFunctionOptions) => boolean;
|
fitView: (options?: ViewportHelperFunctionOptions) => boolean;
|
||||||
@@ -82,8 +82,6 @@ type SvelteFlowStore = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export function createStore({
|
export function createStore({
|
||||||
nodes = [],
|
|
||||||
edges = [],
|
|
||||||
transform = [0, 0, 1],
|
transform = [0, 0, 1],
|
||||||
nodeOrigin = [0, 0],
|
nodeOrigin = [0, 0],
|
||||||
fitView: fitViewOnInit = false,
|
fitView: fitViewOnInit = false,
|
||||||
@@ -91,8 +89,8 @@ export function createStore({
|
|||||||
edgeTypes = {},
|
edgeTypes = {},
|
||||||
id = '1'
|
id = '1'
|
||||||
}: CreateStoreProps): SvelteFlowStore {
|
}: CreateStoreProps): SvelteFlowStore {
|
||||||
const nodesStore = writable(nodes.map((n) => ({ ...n, positionAbsolute: n.position })));
|
const nodesStore = writable([] as Node[]);
|
||||||
const edgesStore = writable(edges);
|
const edgesStore = writable([] as Edge[]);
|
||||||
const heightStore = writable(500);
|
const heightStore = writable(500);
|
||||||
const widthStore = writable(500);
|
const widthStore = writable(500);
|
||||||
const nodeOriginStore = writable(nodeOrigin);
|
const nodeOriginStore = writable(nodeOrigin);
|
||||||
@@ -119,6 +117,7 @@ export function createStore({
|
|||||||
smoothstep: edgeTypes.smoothstep || SmoothStepEdge,
|
smoothstep: edgeTypes.smoothstep || SmoothStepEdge,
|
||||||
default: edgeTypes.default || BezierEdge
|
default: edgeTypes.default || BezierEdge
|
||||||
});
|
});
|
||||||
|
const transformStore = writable(transform);
|
||||||
|
|
||||||
let fitViewOnInitDone = false;
|
let fitViewOnInitDone = false;
|
||||||
|
|
||||||
@@ -171,7 +170,25 @@ export function createStore({
|
|||||||
.filter((e) => e !== null) as EdgeWithData[];
|
.filter((e) => e !== null) as EdgeWithData[];
|
||||||
});
|
});
|
||||||
|
|
||||||
const transformStore = writable(transform);
|
function setEdges(edges: Edge[]) {
|
||||||
|
edgesStore.set(edges);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setNodes(nodes: Node[]) {
|
||||||
|
nodesStore.update((currentNodes) => {
|
||||||
|
const nextNodes = nodes.map((n) => {
|
||||||
|
const currentNode = currentNodes.find((cn) => cn.id === n.id) || {};
|
||||||
|
|
||||||
|
return {
|
||||||
|
...currentNode,
|
||||||
|
...n,
|
||||||
|
positionAbsolute: n.position
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return nextNodes;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function updateNodePositions(nodeDragItems: NodeDragItem[], dragging = false) {
|
function updateNodePositions(nodeDragItems: NodeDragItem[], dragging = false) {
|
||||||
nodesStore.update((nds) => {
|
nodesStore.update((nds) => {
|
||||||
@@ -310,6 +327,8 @@ export function createStore({
|
|||||||
selectionMode,
|
selectionMode,
|
||||||
nodeTypesStore,
|
nodeTypesStore,
|
||||||
edgeTypesStore,
|
edgeTypesStore,
|
||||||
|
setNodes,
|
||||||
|
setEdges,
|
||||||
updateNodePositions,
|
updateNodePositions,
|
||||||
updateNodeDimensions,
|
updateNodeDimensions,
|
||||||
zoomIn,
|
zoomIn,
|
||||||
|
|||||||
@@ -8,4 +8,5 @@ export type SvelteFlowProps = {
|
|||||||
fitView?: boolean;
|
fitView?: boolean;
|
||||||
class?: string;
|
class?: string;
|
||||||
style?: string;
|
style?: string;
|
||||||
|
id?: string;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ export type Node<NodeData = any> = {
|
|||||||
position: XYPosition;
|
position: XYPosition;
|
||||||
sourcePosition?: Position;
|
sourcePosition?: Position;
|
||||||
targetPosition?: Position;
|
targetPosition?: Position;
|
||||||
|
positionAbsolute?: XYPosition;
|
||||||
|
|
||||||
width?: number;
|
width?: number;
|
||||||
height?: number;
|
height?: number;
|
||||||
@@ -35,7 +36,7 @@ export type NodeProps<NodeData = any> = Pick<
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type WrapNodeProps = Node & {
|
export type WrapNodeProps = Node & {
|
||||||
resizeObserver?: ResizeObserver;
|
resizeObserver?: ResizeObserver | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type NodeTypes = Record<string, typeof SvelteComponentTyped<NodeProps>>;
|
export type NodeTypes = Record<string, typeof SvelteComponentTyped<Partial<NodeProps>>>;
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { Node, Edge } from '../lib/types';
|
import type { Node, Edge, NodeTypes } from '../lib/types';
|
||||||
import SvelteFlow, { Controls, Background, BackgroundVariant, Minimap } from '../lib/index';
|
import SvelteFlow, { Controls, Background, BackgroundVariant, Minimap, Panel } from '../lib/index';
|
||||||
import CustomNode from '../customnodes/Custom.svelte';
|
import CustomNode from '../customnodes/Custom.svelte';
|
||||||
|
|
||||||
const nodeTypes = {
|
const nodeTypes: NodeTypes = {
|
||||||
custom: CustomNode,
|
custom: CustomNode,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -43,7 +43,7 @@
|
|||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
const nodes: Node<{ label: string }>[] = [{
|
let nodes: Node<{ label: string }>[] = [{
|
||||||
id: '1',
|
id: '1',
|
||||||
type: 'input',
|
type: 'input',
|
||||||
data: { label: 'Input Node' },
|
data: { label: 'Input Node' },
|
||||||
@@ -79,7 +79,7 @@
|
|||||||
position: { x: 150, y: 300 },
|
position: { x: 150, y: 300 },
|
||||||
}];
|
}];
|
||||||
|
|
||||||
const edges: Edge[] = [{
|
let edges: Edge[] = [{
|
||||||
id: '1-2',
|
id: '1-2',
|
||||||
type: 'default',
|
type: 'default',
|
||||||
source: '1',
|
source: '1',
|
||||||
@@ -95,12 +95,23 @@
|
|||||||
source: '2',
|
source: '2',
|
||||||
target: '4'
|
target: '4'
|
||||||
}];
|
}];
|
||||||
|
|
||||||
|
function updateNode() {
|
||||||
|
nodes[0].position = { x: nodes[0].position.x + 20, y: nodes[0].position.y };
|
||||||
|
}
|
||||||
|
|
||||||
|
// $: {
|
||||||
|
// console.log('nodes changed', nodes)
|
||||||
|
// }
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<SvelteFlow {nodes} {edges} {nodeTypes} fitView>
|
<SvelteFlow bind:nodes bind:edges {nodeTypes} fitView>
|
||||||
<Controls />
|
<Controls />
|
||||||
<Background variant={BackgroundVariant.Dots} />
|
<Background variant={BackgroundVariant.Dots} />
|
||||||
<Minimap />
|
<Minimap />
|
||||||
|
<Panel position="top-right">
|
||||||
|
<button on:click={updateNode}>update node pos</button>
|
||||||
|
</Panel>
|
||||||
</SvelteFlow>
|
</SvelteFlow>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|||||||
Reference in New Issue
Block a user