feat(svelte): add subflows

This commit is contained in:
moklick
2023-03-15 18:34:07 +01:00
parent 9e961809ee
commit 77d14f9ad0
15 changed files with 347 additions and 84 deletions
+5 -3
View File
@@ -89,14 +89,16 @@ export function calcNextPosition(
if (node.parentNode && node.width && node.height) { if (node.parentNode && node.width && node.height) {
const parent = nodeInternals.get(node.parentNode); const parent = nodeInternals.get(node.parentNode);
const parentOrigin = parent?.origin || nodeOrigin; const parentOrigin = parent?.origin || nodeOrigin;
const currNodeOrigin = node.origin || nodeOrigin;
const { x: parentX, y: parentY } = getNodePositionWithOrigin(parent, parentOrigin).positionAbsolute; const { x: parentX, y: parentY } = getNodePositionWithOrigin(parent, parentOrigin).positionAbsolute;
currentExtent = currentExtent =
parent && isNumeric(parentX) && isNumeric(parentY) && isNumeric(parent.width) && isNumeric(parent.height) parent && isNumeric(parentX) && isNumeric(parentY) && isNumeric(parent.width) && isNumeric(parent.height)
? [ ? [
[parentX + node.width * parentOrigin[0], parentY + node.height * parentOrigin[1]], [parentX + node.width * currNodeOrigin[0], parentY + node.height * currNodeOrigin[1]],
[ [
parentX + parent.width - node.width + node.width * parentOrigin[0], parentX + parent.width - node.width + node.width * currNodeOrigin[0],
parentY + parent.height - node.height + node.height * parentOrigin[1], parentY + parent.height - node.height + node.height * currNodeOrigin[1],
], ],
] ]
: currentExtent; : currentExtent;
@@ -5,6 +5,7 @@
'drag-n-drop', 'drag-n-drop',
'overview', 'overview',
'stress', 'stress',
'subflows',
'usesvelteflow', 'usesvelteflow',
'validation' 'validation'
]; ];
@@ -1,10 +1,9 @@
import { get, type Writable } from 'svelte/store'; import { get } from 'svelte/store';
import { drag as d3Drag, type D3DragEvent, type SubjectPosition } from 'd3-drag'; import { drag as d3Drag, type D3DragEvent, type SubjectPosition } from 'd3-drag';
import { select } from 'd3-selection'; import { select } from 'd3-selection';
import type { XYPosition, CoordinateExtent, Transform } from '@reactflow/system'; import type { XYPosition, CoordinateExtent } from '@reactflow/system';
import { getDragItems, hasSelector, calcNextPosition } from './utils'; import { getDragItems, hasSelector, calcNextPosition } from './utils';
import type { Node } from '$lib/types';
import type { SvelteFlowStore } from '$lib/store/types'; import type { SvelteFlowStore } from '$lib/store/types';
export type UseDragData = { dx: number; dy: number }; export type UseDragData = { dx: number; dy: number };
+36 -14
View File
@@ -1,5 +1,11 @@
import type { CoordinateExtent, NodeDragItem, NodeOrigin, XYPosition } from '@reactflow/system'; import {
import { clampPosition, isNumeric } from '@reactflow/utils'; errorMessages,
type CoordinateExtent,
type NodeDragItem,
type NodeOrigin,
type XYPosition
} from '@reactflow/system';
import { clampPosition, devWarn, getNodePositionWithOrigin, isNumeric } from '@reactflow/utils';
import type { Node } from '$lib/types'; import type { Node } from '$lib/types';
@@ -65,16 +71,28 @@ export function calcNextPosition(
node: NodeDragItem | Node, node: NodeDragItem | Node,
nextPosition: XYPosition, nextPosition: XYPosition,
nodes: Node[], nodes: Node[],
nodeExtent?: CoordinateExtent, nodeExtent?: CoordinateExtent
nodeOrigin: NodeOrigin = [0, 0]
): { position: XYPosition; positionAbsolute: XYPosition } { ): { position: XYPosition; positionAbsolute: XYPosition } {
let currentExtent = node.extent || nodeExtent; let currentExtent = node.extent || nodeExtent;
if (node.extent === 'parent') { if (node.extent === 'parent') {
if (node.parentNode && node.width && node.height) { if (node.parentNode && node.width && node.height) {
const parent = nodes.find((n) => n.id === node.parentNode)!; const parent = nodes.find((n) => n.id === node.parentNode);
const parentOrigin = parent.origin || nodeOrigin; const parentOrigin = parent?.origin || [0, 0];
const { x: parentX, y: parentY } = parent.positionAbsolute!; const nodeOrigin = node.origin || [0, 0];
const { x: parentX, y: parentY } = getNodePositionWithOrigin(
parent,
parentOrigin
).positionAbsolute;
console.log({
parentX,
parentY,
parentW: parent?.width,
parentH: parent?.height,
nodeW: node.width,
nodeH: node.height,
parentOrigin: parentOrigin[0]
});
currentExtent = currentExtent =
parent && parent &&
isNumeric(parentX) && isNumeric(parentX) &&
@@ -82,19 +100,23 @@ export function calcNextPosition(
isNumeric(parent.width) && isNumeric(parent.width) &&
isNumeric(parent.height) isNumeric(parent.height)
? [ ? [
[parentX + node.width * parentOrigin[0], parentY + node.height * parentOrigin[1]], [parentX + node.width * nodeOrigin[0], parentY + node.height * nodeOrigin[1]],
[ [
parentX + parent.width! - node.width + node.width * parentOrigin[0], parentX + parent.width - node.width + node.width * nodeOrigin[0],
parentY + parent.height! - node.height + node.height * parentOrigin[1] parentY + parent.height - node.height + node.height * nodeOrigin[1]
] ]
] ]
: currentExtent; : currentExtent;
} else { } else {
devWarn('005', errorMessages['005']());
currentExtent = nodeExtent; currentExtent = nodeExtent;
} }
} else if (node.extent && node.parentNode) { } else if (node.extent && node.parentNode) {
const parent = nodes.find((n) => n.id === node.parentNode)!; const parent = nodes.find((n) => n.id === node.parentNode);
const { x: parentX, y: parentY } = parent.positionAbsolute!; const { x: parentX, y: parentY } = getNodePositionWithOrigin(
parent,
parent?.origin || [0, 0]
).positionAbsolute;
currentExtent = [ currentExtent = [
[node.extent[0][0] + parentX, node.extent[0][1] + parentY], [node.extent[0][0] + parentX, node.extent[0][1] + parentY],
[node.extent[1][0] + parentX, node.extent[1][1] + parentY] [node.extent[1][0] + parentX, node.extent[1][1] + parentY]
@@ -104,8 +126,8 @@ export function calcNextPosition(
let parentPosition = { x: 0, y: 0 }; let parentPosition = { x: 0, y: 0 };
if (node.parentNode) { if (node.parentNode) {
const parent = nodes.find((n) => n.id === node.parentNode)!; const parent = nodes.find((n) => n.id === node.parentNode);
parentPosition = parent.positionAbsolute!; parentPosition = getNodePositionWithOrigin(parent, parent?.origin || [0, 0]).positionAbsolute;
} }
const positionAbsolute = currentExtent const positionAbsolute = currentExtent
@@ -22,11 +22,11 @@
export let width: NodeWrapperProps['width'] = undefined; export let width: NodeWrapperProps['width'] = undefined;
export let height: NodeWrapperProps['height'] = undefined; export let height: NodeWrapperProps['height'] = undefined;
export let type: NodeWrapperProps['type'] = 'default'; export let type: NodeWrapperProps['type'] = 'default';
export let isParent: NodeWrapperProps['isParent'] = false;
export let positionAbsolute: NodeWrapperProps['positionAbsolute'] = undefined; export let positionAbsolute: NodeWrapperProps['positionAbsolute'] = undefined;
export let positionOrigin: NodeWrapperProps['positionOrigin'] = undefined; export let positionOrigin: NodeWrapperProps['positionOrigin'] = undefined;
export let sourcePosition: NodeWrapperProps['sourcePosition'] = undefined; export let sourcePosition: NodeWrapperProps['sourcePosition'] = undefined;
export let targetPosition: NodeWrapperProps['targetPosition'] = undefined; export let targetPosition: NodeWrapperProps['targetPosition'] = undefined;
let className: string = ''; let className: string = '';
export { className as class }; export { className as class };
@@ -77,12 +77,13 @@
use:drag={{ nodeId: id, nodes, snapGrid, transform, updateNodePositions }} use:drag={{ nodeId: id, nodes, snapGrid, transform, updateNodePositions }}
bind:this={nodeRef} bind:this={nodeRef}
data-id={id} data-id={id}
class={cc(['svelte-flow__node', `svelte-flow__node-${type}`, className])} class={cc(['svelte-flow__node', `svelte-flow__node-${type || 'default'}`, className])}
class:initializing={!width && !height} class:initializing={!width && !height}
class:dragging class:dragging
class:selected class:selected
class:draggable class:draggable
class:connectable class:connectable
class:parent={isParent}
style:transform={`translate(${positionOrigin?.x ?? 0}px, ${positionOrigin?.y ?? 0}px)`} style:transform={`translate(${positionOrigin?.x ?? 0}px, ${positionOrigin?.y ?? 0}px)`}
{style} {style}
on:click={onSelectNodeHandler} on:click={onSelectNodeHandler}
@@ -21,4 +21,5 @@ export type NodeWrapperProps = Pick<
positionOrigin?: XYPosition; positionOrigin?: XYPosition;
'on:nodeclick'?: (event: MouseEvent) => void; 'on:nodeclick'?: (event: MouseEvent) => void;
resizeObserver?: ResizeObserver | null; resizeObserver?: ResizeObserver | null;
isParent?: boolean;
}; };
@@ -1,5 +1,6 @@
<script lang="ts"> <script lang="ts">
import { onDestroy } from 'svelte'; import { onDestroy } from 'svelte';
import { internalsSymbol } from '@reactflow/system';
import { getPositionWithOrigin } from '@reactflow/utils'; import { getPositionWithOrigin } from '@reactflow/utils';
import { NodeWrapper } from '$lib/components/NodeWrapper'; import { NodeWrapper } from '$lib/components/NodeWrapper';
@@ -41,6 +42,7 @@
connectable={node.connectable || node.connectable === undefined} connectable={node.connectable || node.connectable === undefined}
positionAbsolute={node.positionAbsolute} positionAbsolute={node.positionAbsolute}
positionOrigin={posOrigin} positionOrigin={posOrigin}
isParent={!!node[internalsSymbol]?.isParent}
width={node.width} width={node.width}
height={node.height} height={node.height}
style={node.style} style={node.style}
+43 -43
View File
@@ -8,55 +8,55 @@ import type { SvelteFlowStoreState } from './types';
export function getEdgesLayouted(store: SvelteFlowStoreState) { export function getEdgesLayouted(store: SvelteFlowStoreState) {
return derived([store.edges, store.nodes], ([edges, nodes]) => { return derived([store.edges, store.nodes], ([edges, nodes]) => {
return edges return edges.reduce<EdgeLayouted[]>((res, edge) => {
.map((edge) => { const sourceNode = nodes.find((node) => node.id === edge.source);
const sourceNode = nodes.find((node) => node.id === edge.source); const targetNode = nodes.find((node) => node.id === edge.target);
const targetNode = nodes.find((node) => node.id === edge.target); const [sourceNodeRect, sourceHandleBounds, sourceIsValid] = getNodeData(sourceNode);
const [sourceNodeRect, sourceHandleBounds, sourceIsValid] = getNodeData(sourceNode); const [targetNodeRect, targetHandleBounds, targetIsValid] = getNodeData(targetNode);
const [targetNodeRect, targetHandleBounds, targetIsValid] = getNodeData(targetNode);
if (!sourceIsValid || !targetIsValid) { if (!sourceIsValid || !targetIsValid) {
return null; return res;
} }
const edgeType = edge.type || 'default'; const edgeType = edge.type || 'default';
const targetNodeHandles = targetHandleBounds!.target; const targetNodeHandles = targetHandleBounds!.target;
const sourceHandle = getHandle(sourceHandleBounds!.source!, edge.sourceHandle); const sourceHandle = getHandle(sourceHandleBounds!.source!, edge.sourceHandle);
const targetHandle = getHandle(targetNodeHandles!, edge.targetHandle); const targetHandle = getHandle(targetNodeHandles!, edge.targetHandle);
const sourcePosition = sourceHandle?.position || Position.Bottom; const sourcePosition = sourceHandle?.position || Position.Bottom;
const targetPosition = targetHandle?.position || Position.Top; const targetPosition = targetHandle?.position || Position.Top;
if (!sourceHandle || !targetHandle) { if (!sourceHandle || !targetHandle) {
return null; return res;
} }
const { sourceX, sourceY, targetX, targetY } = getEdgePositions( const { sourceX, sourceY, targetX, targetY } = getEdgePositions(
sourceNodeRect, sourceNodeRect,
sourceHandle, sourceHandle,
sourcePosition, sourcePosition,
targetNodeRect, targetNodeRect,
targetHandle, targetHandle,
targetPosition targetPosition
); );
// we nee to do this to match the types // we nee to do this to match the types
const sourceHandleId = edge.sourceHandle; const sourceHandleId = edge.sourceHandle;
const targetHandleId = edge.targetHandle; const targetHandleId = edge.targetHandle;
return { res.push({
...edge, ...edge,
type: edgeType, type: edgeType,
sourceX, sourceX,
sourceY, sourceY,
targetX, targetX,
targetY, targetY,
sourcePosition, sourcePosition,
targetPosition, targetPosition,
sourceHandleId, sourceHandleId,
targetHandleId targetHandleId
}; } as EdgeLayouted);
})
.filter((e) => e !== null) as EdgeLayouted[]; return res;
}, []);
}); });
} }
+18 -11
View File
@@ -19,7 +19,7 @@ import {
} from '@reactflow/utils'; } from '@reactflow/utils';
import { addEdge as addEdgeUtil } from '$lib/utils'; import { addEdge as addEdgeUtil } from '$lib/utils';
import type { EdgeTypes, NodeTypes, Node, Edge, ConnectionData } from '$lib/types'; import type { EdgeTypes, NodeTypes, Node, Edge, ConnectionData, FitViewOptions } from '$lib/types';
import { getEdgesLayouted } from './edges-layouted'; import { getEdgesLayouted } from './edges-layouted';
import { getConnectionPath } from './connection-path'; import { getConnectionPath } from './connection-path';
import { import {
@@ -102,15 +102,20 @@ export function createStore(params: CreateStoreParams): SvelteFlowStore {
); );
if (doUpdate) { if (doUpdate) {
node[internalsSymbol] = { const newNode = {
...node[internalsSymbol], ...node,
handleBounds: { width: dimensions.width,
source: getHandleBounds('.source', update.nodeElement, zoom, node.origin), height: dimensions.height,
target: getHandleBounds('.target', update.nodeElement, zoom, node.origin) [internalsSymbol]: {
...node[internalsSymbol],
handleBounds: {
source: getHandleBounds('.source', update.nodeElement, zoom, node.origin),
target: getHandleBounds('.target', update.nodeElement, zoom, node.origin)
}
} }
}; };
node.width = dimensions.width;
node.height = dimensions.height; return newNode;
} }
} }
@@ -121,7 +126,7 @@ export function createStore(params: CreateStoreParams): SvelteFlowStore {
const fitViewOnInitDone = const fitViewOnInitDone =
get(store.fitViewOnInitDone) || get(store.fitViewOnInitDone) ||
(get(store.fitViewOnInit) && !!d3Zoom && !!d3Selection && fitView()); (get(store.fitViewOnInit) && !!d3Zoom && !!d3Selection && fitView({ nodes: nextNodes }));
store.fitViewOnInitDone.set(fitViewOnInitDone); store.fitViewOnInitDone.set(fitViewOnInitDone);
store.nodes.set(nextNodes); store.nodes.set(nextNodes);
@@ -162,16 +167,18 @@ export function createStore(params: CreateStoreParams): SvelteFlowStore {
} }
} }
function fitView() { function fitView(options?: FitViewOptions) {
const { zoom: d3Zoom, selection: d3Selection } = get(store.d3); const { zoom: d3Zoom, selection: d3Selection } = get(store.d3);
if (!d3Zoom || !d3Selection) { if (!d3Zoom || !d3Selection) {
return false; return false;
} }
const fitViewNodes = options?.nodes || get(store.nodes);
return fitViewUtil( return fitViewUtil(
{ {
nodes: get(store.nodes), nodes: fitViewNodes as Node[],
width: get(store.width), width: get(store.width),
height: get(store.height), height: get(store.height),
minZoom: 0.2, minZoom: 0.2,
+2 -2
View File
@@ -7,7 +7,7 @@ import type {
} from '@reactflow/system'; } from '@reactflow/system';
import type { initialStoreState } from './initial-store'; import type { initialStoreState } from './initial-store';
import type { Node, Edge, ConnectionData, NodeTypes, EdgeTypes } from '$lib/types'; import type { Node, Edge, ConnectionData, NodeTypes, EdgeTypes, FitViewOptions } from '$lib/types';
import type { Writable } from 'svelte/store'; import type { Writable } from 'svelte/store';
export type SvelteFlowStoreActions = { export type SvelteFlowStoreActions = {
@@ -18,7 +18,7 @@ export type SvelteFlowStoreActions = {
zoomOut: (options?: ViewportHelperFunctionOptions) => void; zoomOut: (options?: ViewportHelperFunctionOptions) => void;
setMinZoom: (minZoom: number) => void; setMinZoom: (minZoom: number) => void;
setMaxZoom: (maxZoom: number) => void; setMaxZoom: (maxZoom: number) => void;
fitView: (options?: ViewportHelperFunctionOptions) => boolean; fitView: (options?: FitViewOptions) => boolean;
updateNodePositions: ( updateNodePositions: (
nodeDragItems: NodeDragItem[], nodeDragItems: NodeDragItem[],
positionChanged?: boolean, positionChanged?: boolean,
+68 -6
View File
@@ -1,3 +1,4 @@
import { writable, type Writable } from 'svelte/store';
import { import {
isNodeBase, isNodeBase,
isEdgeBase, isEdgeBase,
@@ -5,10 +6,12 @@ import {
getOutgoersBase, getOutgoersBase,
getIncomersBase, getIncomersBase,
updateEdgeBase, updateEdgeBase,
getConnectedEdgesBase getConnectedEdgesBase,
isNumeric,
getNodePositionWithOrigin
} from '@reactflow/utils'; } from '@reactflow/utils';
import { internalsSymbol, type XYZPosition } from '@reactflow/system';
import type { DefaultEdgeOptions, DefaultNodeOptions, 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 isNode = isNodeBase<Node, Edge>;
export const isEdge = isEdgeBase<Node, Edge>; export const isEdge = isEdgeBase<Node, Edge>;
@@ -27,11 +30,53 @@ export const createNodes = (
let defaults = defaultOptions || {}; let defaults = defaultOptions || {};
const _set: typeof set = (nds: Node[]) => { const _set: typeof set = (nds: Node[]) => {
const nextNodes = defaults ? nds.map((node) => ({ ...defaults, ...node })) : nds; const parentNodes: Record<string, boolean> = {};
// @todo calculate absolute position based on parent / child relation
const nextNodesLayouted = nextNodes.map((n) => ({ ...n, positionAbsolute: n.position }));
value = nextNodesLayouted; const nextNodes = nds.map((n) => {
const node: Node = { ...defaults, ...n, positionAbsolute: n.position };
const z = (isNumeric(node.zIndex) ? node.zIndex : 0) + (node.selected ? 1 : 0);
if (node.parentNode) {
parentNodes[node.parentNode] = true;
}
Object.defineProperty(node, internalsSymbol, {
value: {
handleBounds: node?.[internalsSymbol]?.handleBounds,
z
}
});
return node;
});
const nodesWithPositions = nextNodes.map((node) => {
if (node.parentNode && !parentNodes[node.parentNode]) {
throw new Error(`Parent node ${node.parentNode} not found`);
}
if (node.parentNode || parentNodes?.[node.id]) {
const { x, y, z } = calculateXYZPosition(node, nextNodes, {
...node.position,
z: node[internalsSymbol]?.z ?? 0
});
node.positionAbsolute = {
x,
y
};
node[internalsSymbol]!.z = z;
if (parentNodes?.[node.id]) {
node[internalsSymbol]!.isParent = true;
}
}
return node;
});
value = nodesWithPositions;
set(value); set(value);
}; };
@@ -81,3 +126,20 @@ export const createEdges = (
setDefaultOptions setDefaultOptions
}; };
}; };
function calculateXYZPosition(node: Node, nodes: Node[], result: XYZPosition): XYZPosition {
if (!node.parentNode) {
return result;
}
const parentNode = nodes.find((n) => n.id === node.parentNode)!;
const parentNodePosition = getNodePositionWithOrigin(parentNode, parentNode?.origin);
return calculateXYZPosition(parentNode, nodes, {
x: (result.x ?? 0) + parentNodePosition.x,
y: (result.y ?? 0) + parentNodePosition.y,
z:
(parentNode[internalsSymbol]?.z ?? 0) > (result.z ?? 0)
? parentNode[internalsSymbol]?.z ?? 0
: result.z ?? 0
});
}
@@ -0,0 +1,141 @@
<script lang="ts">
import SvelteFlow, {
SvelteFlowProvider,
Controls,
Background,
BackgroundVariant,
Minimap,
createNodes,
createEdges,
type NodeTypes,
} from '../../lib/index';
import { DebugNode } from './DebugNode';
const nodeTypes: NodeTypes = {
default: DebugNode,
};
const nodes = createNodes([
{
id: '1',
type: 'input',
data: { label: 'Node 1' },
position: { x: 250, y: 5 },
origin: [0.5, 0.5],
},
{
id: '4',
data: { label: 'Node 4' },
position: { x: 100, y: 200 },
style: "width:500px; height:300px;"
},
{
id: '4a',
data: { label: 'Node 4a' },
position: { x: 15, y: 15 },
parentNode: '4',
extent: [
[0, 0],
[100, 100],
],
},
{
id: '4b',
data: { label: 'Node 4b' },
position: { x: 100, y: 60 },
style: "width: 300px; height: 200px;",
parentNode: '4',
},
{
id: '4b1',
data: { label: 'Node 4b1' },
position: { x: 40, y: 20 },
parentNode: '4b',
},
{
id: '4b2',
data: { label: 'Node 4b2' },
position: { x: 20, y: 100 },
parentNode: '4b',
},
{
id: '5',
type: 'group',
data: { label: 'Node 5' },
position: { x: 650, y: 250 },
style: "width: 400px; height: 150px",
zIndex: 1000,
},
{
id: '5a',
data: { label: 'Node 5a' },
position: { x: 0, y: 0 },
parentNode: '5',
extent: 'parent',
},
{
id: '5b',
data: { label: 'Node 5b' },
position: { x: 225, y: 50 },
parentNode: '5',
expandParent: true,
},
{
id: '2',
data: { label: 'Node 2' },
position: { x: 100, y: 100 },
},
{
id: '3',
data: { label: 'Node 3' },
position: { x: 400, y: 100 },
}
]);
const edges = createEdges([
{
id: 'e1-2',
source: '1',
target: '2',
// markerEnd: {
// type: MarkerType.Arrow,
// strokeWidth: 2,
// width: 15,
// height: 15,
// color: '#f00',
// },
},
{ id: 'e1-3', source: '1', target: '3' },
{ id: 'e3-4', source: '3', target: '4', zIndex: 100 },
{ id: 'e3-4b', source: '3', target: '4b' },
{ id: 'e4a-4b1', source: '4a', target: '4b1' },
{ id: 'e4a-4b2', source: '4a', target: '4b2', zIndex: 100 },
{ id: 'e4b1-4b2', source: '4b1', target: '4b2' },
], { animated: true });
$: {
console.log($nodes)
}
</script>
<SvelteFlowProvider
{nodes}
{edges}
>
<SvelteFlow
{nodeTypes}
fitView
minZoom={0.1}
maxZoom={2.5}
>
<Controls />
<Background variant={BackgroundVariant.Dots} />
<Minimap />
</SvelteFlow>
</SvelteFlowProvider>
<style>
:global(.svelte-flow .svelte-flow__node.parent) {
background-color: rgba(220,220,255,0.4)
}
</style>
@@ -0,0 +1,23 @@
<script lang="ts">
import { Handle, Position } from '../../../lib/index';
export let id: string;
export let xPos: number = 0;
export let yPos: number = 0;
export let zIndex: number = 0;
</script>
<Handle type="target" position={Position.Top} />
<div>{id}</div>
<div>
x:{Math.round(xPos || 0)} y:{Math.round(yPos || 0)} z:{zIndex}
</div>
<Handle type="source" position={Position.Bottom} />
<style>
.custom {
background: #ffcc00;
padding: 10px;
}
</style>
@@ -0,0 +1 @@
export { default as DebugNode } from './DebugNode.svelte';
+1
View File
@@ -80,6 +80,7 @@ export type NodeDragItem = {
extent?: 'parent' | CoordinateExtent; extent?: 'parent' | CoordinateExtent;
parentNode?: string; parentNode?: string;
dragging?: boolean; dragging?: boolean;
origin?: NodeOrigin;
}; };
export type NodeOrigin = [number, number]; export type NodeOrigin = [number, number];