fixed subflows, cleaned up nodeRenderer, cleaned up nodewrapper

This commit is contained in:
peterkogo
2024-12-11 13:38:48 +01:00
parent dca2eb6f37
commit 1c67bf848f
10 changed files with 370 additions and 384 deletions
@@ -1,243 +1,8 @@
<script lang="ts">
import {
SvelteFlow,
Controls,
Background,
BackgroundVariant,
MiniMap,
Panel,
SelectionMode,
type NodeTypes,
type EdgeTypes,
type Node,
type Edge,
ConnectionMode,
ControlButton,
type FitViewOptions
} from '@xyflow/svelte';
import CustomNode from './CustomNode.svelte';
import CustomNodeDragHandle from './CustomNodeDragHandle.svelte';
import CustomEdge from './CustomEdge.svelte';
import '@xyflow/svelte/dist/style.css';
import InitTracker from './InitTracker.svelte';
const nodeTypes: NodeTypes = {
custom: CustomNode,
dragHandle: CustomNodeDragHandle
};
const edgeTypes: EdgeTypes = {
custom: CustomEdge
};
const fitViewOptions: FitViewOptions = {
padding: 0.2,
nodes: [{ id: '1' }, { id: '2' }]
};
let nodes = $state.raw<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 },
selectable: false
},
{
id: 'A',
type: 'default',
data: { label: 'Styled with class' },
class: 'custom-style',
position: { x: 150, y: 150 }
},
{
id: 'D',
type: 'default',
data: { label: 'Not draggable' },
position: { x: 150, y: 200 },
draggable: false
},
{
id: '3',
type: 'output',
data: { label: 'Output Node' },
position: { x: 300, y: 150 }
},
{
id: 'B',
type: 'default',
data: { label: 'Styled with style' },
style: 'border: 2px solid #ff5050;',
position: { x: 450, y: 150 }
},
{
id: 'C',
type: 'dragHandle',
data: { label: 'custom drag handle' },
dragHandle: '.custom-drag-handle',
position: { x: 450, y: 250 }
},
{
id: '4',
type: 'custom',
data: { label: 'Custom Node' },
position: { x: 150, y: 300 }
},
{
id: 'hideunhide',
data: { label: 'HIDE ME' },
position: { x: 300, y: 75 },
hidden: true
}
]);
let edges = $state.raw<Edge[]>([
{
id: '1-2',
type: 'default',
source: '1',
target: '2',
label: 'Edge Text'
},
{
id: '1-3',
type: 'smoothstep',
source: '1',
target: '3',
selectable: false
},
{
id: '2-4',
type: 'custom',
source: '2',
target: '4',
animated: true
}
]);
function updateNode() {
// We might as well just use updateNode() here
// this is just to show what is required to update a node
const newNode = { ...nodes[0] };
newNode.position.x += 20;
nodes[0] = newNode;
nodes = [...nodes];
}
function updateEdge() {
// We might as well just use updateEdge() here
// this is just to show what is required to update an edge
const newEdge = { ...edges[0] };
newEdge.type = newEdge.type === 'default' ? 'smoothstep' : 'default';
edges[0] = newEdge;
edges = [...edges];
}
import { SvelteFlowProvider } from '@xyflow/svelte';
import Flow from './Flow.svelte';
</script>
<SvelteFlow
bind:nodes
bind:edges
{nodeTypes}
{edgeTypes}
fitView
fitViewOptions={{
padding: 0.1,
nodes: [{ id: '1' }, { id: '2' }, { id: '3' }]
}}
minZoom={0}
maxZoom={Infinity}
selectionMode={SelectionMode.Full}
initialViewport={{ x: 100, y: 100, zoom: 2 }}
snapGrid={[25, 25]}
oninit={() => console.log('on init')}
onnodeclick={(event) => console.log('on node click', event)}
onnodemouseenter={(event) => console.log('on node enter', event)}
onnodemouseleave={(event) => console.log('on node leave', event)}
onedgeclick={(event) => console.log('edge click', event)}
onedgemouseenter={(event) => console.log('edge enter', event)}
onedgemouseleave={(event) => console.log('edge leave', event)}
onconnectstart={(event) => console.log('on connect start', event)}
onconnect={(event) => console.log('on connect', event)}
onconnectend={(event) => console.log('on connect end', event)}
onpaneclick={(event) => console.log('on pane click', event)}
onpanecontextmenu={(event) => {
console.log('on pane contextmenu', event);
}}
onnodedrag={(event) => {
console.log('on node drag', event);
}}
onnodedragstart={(event) => {
console.log('on node drag start', event);
}}
onnodedragstop={({ event }) => {
console.log('on node drag stop', event);
}}
onnodecontextmenu={({ event }) => {
event.preventDefault();
console.log('on node contextmenu', event);
}}
onedgecontextmenu={({ event, edge }) => {
event.preventDefault();
console.log('on edge contextmenu', edge);
}}
onselectionclick={(event) => console.log('on selection click', event)}
onselectioncontextmenu={(event) => console.log('on selection contextmenu', event)}
onbeforedelete={async ({ nodes, edges }) => {
console.log('on before delete', nodes, edges);
const deleteElements = confirm('Are you sure you want to delete the selected elements?');
return deleteElements;
}}
autoPanOnConnect
autoPanOnNodeDrag
connectionMode={ConnectionMode.Strict}
attributionPosition={'top-center'}
deleteKey={['Backspace', 'd']}
>
<Controls orientation="horizontal" {fitViewOptions}>
{#snippet before()}
<ControlButton>xy</ControlButton>
{/snippet}
<ControlButton aria-label="log" onclick={() => console.log('control button')}>log</ControlButton
>
</Controls>
<Background variant={BackgroundVariant.Dots} />
<MiniMap />
<Panel position="top-right">
<button onclick={updateNode}>update node pos</button>
<button onclick={updateEdge}>update edge type</button>
<button
onclick={() => {
$nodes[$nodes.length - 1].hidden = !$nodes[$nodes.length - 1].hidden;
$nodes = $nodes;
}}>hide/unhide</button
>
</Panel>
<InitTracker />
</SvelteFlow>
<style>
:global(.svelte-flow .custom-style) {
background: #ff5050;
color: white;
}
:root {
--background-color: #ffffdd;
--background-pattern-color: #5050ff;
--minimap-background-color: #f5f6f7;
--minimap-mask-color: rgb(255, 255, 240, 0.6);
--controls-button-background-color: #ddd;
--controls-button-background-color-hover: #ccc;
}
</style>
<SvelteFlowProvider>
<Flow />
</SvelteFlowProvider>
@@ -0,0 +1,245 @@
<script lang="ts">
import {
SvelteFlow,
Controls,
Background,
BackgroundVariant,
MiniMap,
Panel,
SelectionMode,
type NodeTypes,
type EdgeTypes,
type Node,
type Edge,
ConnectionMode,
ControlButton,
type FitViewOptions,
useSvelteFlow
} from '@xyflow/svelte';
import CustomNode from './CustomNode.svelte';
import CustomNodeDragHandle from './CustomNodeDragHandle.svelte';
import CustomEdge from './CustomEdge.svelte';
import '@xyflow/svelte/dist/style.css';
import InitTracker from './InitTracker.svelte';
const nodeTypes: NodeTypes = {
custom: CustomNode,
dragHandle: CustomNodeDragHandle
};
const edgeTypes: EdgeTypes = {
custom: CustomEdge
};
const fitViewOptions: FitViewOptions = {
padding: 0.2,
nodes: [{ id: '1' }, { id: '2' }]
};
let nodes = $state.raw<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 },
selectable: false
},
{
id: 'A',
type: 'default',
data: { label: 'Styled with class' },
class: 'custom-style',
position: { x: 150, y: 150 }
},
{
id: 'D',
type: 'default',
data: { label: 'Not draggable' },
position: { x: 150, y: 200 },
draggable: false
},
{
id: '3',
type: 'output',
data: { label: 'Output Node' },
position: { x: 300, y: 150 }
},
{
id: 'B',
type: 'default',
data: { label: 'Styled with style' },
style: 'border: 2px solid #ff5050;',
position: { x: 450, y: 150 }
},
{
id: 'C',
type: 'dragHandle',
data: { label: 'custom drag handle' },
dragHandle: '.custom-drag-handle',
position: { x: 450, y: 250 }
},
{
id: '4',
type: 'custom',
data: { label: 'Custom Node' },
position: { x: 150, y: 300 }
},
{
id: 'hideunhide',
data: { label: 'HIDE ME' },
position: { x: 300, y: 75 },
hidden: true
}
]);
let edges = $state.raw<Edge[]>([
{
id: '1-2',
type: 'default',
source: '1',
target: '2',
label: 'Edge Text'
},
{
id: '1-3',
type: 'smoothstep',
source: '1',
target: '3',
selectable: false
},
{
id: '2-4',
type: 'custom',
source: '2',
target: '4',
animated: true
}
]);
function moveNode() {
// We might as well just use updateNode() here
// this is just to show what is required to update a node
const newNode = { ...nodes[0] };
newNode.position.x += 20;
nodes[0] = newNode;
nodes = [...nodes];
}
function changeEdgeType() {
// We might as well just use updateEdge() here
// this is just to show what is required to update an edge
const newEdge = { ...edges[0] };
newEdge.type = newEdge.type === 'default' ? 'smoothstep' : 'default';
edges[0] = newEdge;
edges = [...edges];
}
let { updateNode } = $derived(useSvelteFlow());
function hideUnhide() {
updateNode('hideunhide', (node) => ({ hidden: !node.hidden }));
}
</script>
<SvelteFlow
bind:nodes
bind:edges
{nodeTypes}
{edgeTypes}
fitView
fitViewOptions={{
padding: 0.1,
nodes: [{ id: '1' }, { id: '2' }, { id: '3' }]
}}
minZoom={0}
maxZoom={Infinity}
selectionMode={SelectionMode.Full}
initialViewport={{ x: 100, y: 100, zoom: 2 }}
snapGrid={[25, 25]}
oninit={() => console.log('on init')}
onnodeclick={(event) => console.log('on node click', event)}
onnodemouseenter={(event) => console.log('on node enter', event)}
onnodemouseleave={(event) => console.log('on node leave', event)}
onedgeclick={(event) => console.log('edge click', event)}
onedgemouseenter={(event) => console.log('edge enter', event)}
onedgemouseleave={(event) => console.log('edge leave', event)}
onconnectstart={(event) => console.log('on connect start', event)}
onconnect={(event) => console.log('on connect', event)}
onconnectend={(event) => console.log('on connect end', event)}
onpaneclick={(event) => console.log('on pane click', event)}
onpanecontextmenu={(event) => {
console.log('on pane contextmenu', event);
}}
onnodedrag={(event) => {
console.log('on node drag', event);
}}
onnodedragstart={(event) => {
console.log('on node drag start', event);
}}
onnodedragstop={({ event }) => {
console.log('on node drag stop', event);
}}
onnodecontextmenu={({ event }) => {
event.preventDefault();
console.log('on node contextmenu', event);
}}
onedgecontextmenu={({ event, edge }) => {
event.preventDefault();
console.log('on edge contextmenu', edge);
}}
onselectionclick={(event) => console.log('on selection click', event)}
onselectioncontextmenu={(event) => console.log('on selection contextmenu', event)}
onbeforedelete={async ({ nodes, edges }) => {
console.log('on before delete', nodes, edges);
const deleteElements = confirm('Are you sure you want to delete the selected elements?');
return deleteElements;
}}
autoPanOnConnect
autoPanOnNodeDrag
connectionMode={ConnectionMode.Strict}
attributionPosition={'top-center'}
deleteKey={['Backspace', 'd']}
>
<Controls orientation="horizontal" {fitViewOptions}>
{#snippet before()}
<ControlButton>xy</ControlButton>
{/snippet}
<ControlButton aria-label="log" onclick={() => console.log('control button')}>log</ControlButton
>
</Controls>
<Background variant={BackgroundVariant.Dots} />
<MiniMap />
<Panel position="top-right">
<button onclick={moveNode}>update node pos</button>
<button onclick={changeEdgeType}>update edge type</button>
<button onclick={hideUnhide}>hide/unhide</button>
</Panel>
<InitTracker />
</SvelteFlow>
<style>
:global(.svelte-flow .custom-style) {
background: #ff5050;
color: white;
}
:root {
--background-color: #ffffdd;
--background-pattern-color: #5050ff;
--minimap-background-color: #f5f6f7;
--minimap-mask-color: rgb(255, 255, 240, 0.6);
--controls-button-background-color: #ddd;
--controls-button-background-color-hover: #ccc;
}
</style>
@@ -1,5 +1,4 @@
<script lang="ts">
import { writable } from 'svelte/store';
import {
SvelteFlow,
Controls,
@@ -7,7 +6,8 @@
BackgroundVariant,
MiniMap,
type NodeTypes,
type Node
type Node,
type Edge
} from '@xyflow/svelte';
import '@xyflow/svelte/dist/style.css';
@@ -18,7 +18,7 @@
default: DebugNode
};
const nodes = writable<Node[]>([
let nodes = $state<Node[]>([
{
id: '1',
type: 'input',
@@ -95,7 +95,7 @@
}
]);
const edges = writable([
let edges = $state<Edge[]>([
{
id: 'e1-2',
source: '1',
@@ -115,11 +115,9 @@
{ id: 'e4a-4b2', source: '4a', target: '4b2', zIndex: 100 },
{ id: 'e4b1-4b2', source: '4b1', target: '4b2' }
]);
$inspect($nodes);
</script>
<SvelteFlow {nodes} {edges} {nodeTypes} fitView minZoom={0.1} maxZoom={2.5}>
<SvelteFlow bind:nodes bind:edges {nodeTypes} fitView minZoom={0.1} maxZoom={2.5}>
<Controls />
<Background variant={BackgroundVariant.Dots} />
<MiniMap />
@@ -1,45 +1,18 @@
<script lang="ts">
import { setContext, onDestroy } from 'svelte';
import cc from 'classcat';
import { errorMessages, Position } from '@xyflow/system';
import { errorMessages, nodeHasDimensions, Position } from '@xyflow/system';
import drag from '$lib/actions/drag';
import DefaultNode from '$lib/components/nodes/DefaultNode.svelte';
import type { ConnectableContext, NodeWrapperProps } from './types';
import { getNodeInlineStyleDimensions } from './utils';
import type { NodeEvents } from '$lib/types';
import type { Node, NodeEvents } from '$lib/types';
let {
store,
node,
id,
data = {},
selected = false,
draggable,
selectable,
deletable,
connectable = true,
hidden = false,
dragging = false,
resizeObserver = null,
style,
class: className,
type = 'default',
isParent = false,
parentId,
positionX,
positionY,
sourcePosition,
targetPosition,
zIndex,
measuredWidth,
measuredHeight,
initialWidth,
initialHeight,
width,
height,
dragHandle,
initialized = false,
resizeObserver,
nodeClickDistance,
onnodeclick,
onnodedrag,
@@ -51,6 +24,58 @@
onnodecontextmenu
}: NodeWrapperProps & NodeEvents = $props();
let {
id,
data = {},
selected = false,
draggable: _draggable,
selectable: _selectable,
deletable = true,
connectable: _connectable,
hidden = false,
dragging = false,
style,
class: className,
type = 'default',
parentId,
sourcePosition,
targetPosition,
measured: { width: measuredWidth, height: measuredHeight } = { width: 0, height: 0 },
initialWidth,
initialHeight,
width,
height,
dragHandle
} = $derived(node);
let draggable = $derived(_draggable ?? store.nodesDraggable);
let selectable = $derived(_selectable ?? store.elementsSelectable);
let connectable = $derived(_connectable ?? store.nodesConnectable);
let initialized = $derived(nodeHasDimensions(node));
// TODO: does this make sense
let flipFlop = false;
let shouldRerenderSignal = $derived.by(() =>
store.adoptNodes.has(id) ? (flipFlop = !flipFlop) : flipFlop
);
function getInternalNode(node: Node, shouldRerender: boolean = false) {
return { ...store.nodeLookup.get(node.id)! };
}
let {
internals: {
z: zIndex = 0,
positionAbsolute: { x: positionX, y: positionY }
}
} = $derived(getInternalNode(node, shouldRerenderSignal));
function isInParentLookup(id: string) {
return store.parentLookup.has(id);
}
let isParent = $derived(isInParentLookup(id));
let nodeRef: HTMLDivElement | null = $state(null);
let prevNodeRef: HTMLDivElement | null = null;
@@ -144,7 +169,7 @@
store.handleNodeSelection(id);
}
onnodeclick?.({ node: node.internals.userNode, event });
onnodeclick?.({ node, event });
}
</script>
@@ -5,40 +5,41 @@ export type ConnectableContext = {
value: boolean;
};
export type NodeWrapperProps = Pick<
Node,
| 'id'
| 'class'
| 'connectable'
| 'data'
| 'draggable'
| 'dragging'
| 'selected'
| 'selectable'
| 'deletable'
| 'style'
| 'type'
| 'sourcePosition'
| 'targetPosition'
| 'dragHandle'
| 'hidden'
| 'width'
| 'height'
| 'initialWidth'
| 'initialHeight'
| 'parentId'
> & {
measuredWidth?: number;
measuredHeight?: number;
type: string;
positionX: number;
positionY: number;
'on:nodeclick'?: (event: MouseEvent) => void;
resizeObserver?: ResizeObserver | null;
isParent?: boolean;
zIndex: number;
node: InternalNode;
initialized: boolean;
export type NodeWrapperProps = {
// Pick<
// Node,
// | 'id'
// | 'class'
// | 'connectable'
// | 'data'
// | 'draggable'
// | 'dragging'
// | 'selected'
// | 'selectable'
// | 'deletable'
// | 'style'
// | 'type'
// | 'sourcePosition'
// | 'targetPosition'
// | 'dragHandle'
// | 'hidden'
// | 'width'
// | 'height'
// | 'initialWidth'
// | 'initialHeight'
// | 'parentId'
// > & {
// measuredWidth?: number;
// measuredHeight?: number;
// type: string;
// positionX: number;
// positionY: number;
// 'on:nodeclick'?: (event: MouseEvent) => void;
// isParent?: boolean;
// zIndex: number;
// initialized: boolean;
node: Node;
nodeClickDistance?: number;
resizeObserver?: ResizeObserver | null;
store: SvelteFlowStore;
};
@@ -1,9 +1,8 @@
<script lang="ts">
import { onDestroy } from 'svelte';
import { nodeHasDimensions } from '@xyflow/system';
import type { Node, NodeEvents } from '$lib/types';
import { NodeWrapper } from '$lib/components/NodeWrapper';
import type { NodeEvents } from '$lib/types';
import type { SvelteFlowStore } from '$lib/store/types';
let {
@@ -19,8 +18,6 @@
onnodedragstop
}: { store: SvelteFlowStore; nodeClickDistance?: number } & NodeEvents = $props();
// const { nodes } = store;
const resizeObserver: ResizeObserver | null =
typeof ResizeObserver === 'undefined'
? null
@@ -43,59 +40,13 @@
onDestroy(() => {
resizeObserver?.disconnect();
});
function give(node: Node) {
const internalNode = store.nodeLookup.get(node.id);
// console.log('this reruns most definitely', internalNode);
console.log(internalNode?.position);
return internalNode;
}
// $inspect(store.nodes).with(console.trace);
</script>
<div class="svelte-flow__nodes">
{#each store.nodes as node (node.id)}
{@const internalNode = store.nodeLookup.get(node.id)}
<NodeWrapper
{store}
node={internalNode!}
id={node.id}
data={node.data}
selected={!!node.selected}
hidden={!!node.hidden}
draggable={!!(
node.draggable ||
(store.nodesDraggable && typeof node.draggable === 'undefined')
)}
selectable={!!(
node.selectable ||
(store.elementsSelectable && typeof node.selectable === 'undefined')
)}
connectable={!!(
node.connectable ||
(store.nodesConnectable && typeof node.connectable === 'undefined')
)}
deletable={node.deletable ?? true}
positionX={internalNode!.internals.positionAbsolute.x}
positionY={internalNode!.internals.positionAbsolute.y}
isParent={store.parentLookup.has(node.id)}
style={node.style}
class={node.class}
type={node.type ?? 'default'}
sourcePosition={node.sourcePosition}
targetPosition={node.targetPosition}
dragging={node.dragging}
zIndex={internalNode!.internals.z ?? 0}
dragHandle={node.dragHandle}
initialized={nodeHasDimensions(node)}
width={node.width}
height={node.height}
initialWidth={node.initialWidth}
initialHeight={node.initialHeight}
measuredWidth={node.measured?.width ?? 0}
measuredHeight={node.measured?.height ?? 0}
parentId={node.parentId}
{node}
{resizeObserver}
{nodeClickDistance}
{onnodeclick}
+10 -14
View File
@@ -211,13 +211,11 @@ export function useSvelteFlow(): {
// nodeUpdate: Partial<Node> | ((node: Node) => Partial<Node>),
// options?: { replace: boolean }
// ) => void;
updateNode:
| ((id: string, nodeUpdate: Node | ((node: Node) => Node), options: { replace: true }) => void)
| ((
id: string,
nodeUpdate: Partial<Node> | ((node: Node) => Partial<Node>),
options: { replace: false }
) => void);
updateNode: (
id: string,
nodeUpdate: Partial<Node> | ((node: Node) => Partial<Node>),
options?: { replace: boolean }
) => void;
/**
* Updates the data attribute of a node.
*
@@ -248,13 +246,11 @@ export function useSvelteFlow(): {
* @example
* updateNode('node-1', (node) => ({ position: { x: node.position.x + 10, y: node.position.y } }));
*/
updateEdge:
| ((id: string, edgeUpdate: Edge | ((edge: Edge) => Edge), options: { replace: true }) => void)
| ((
id: string,
edgeUpdate: Partial<Edge> | ((edge: Edge) => Partial<Edge>),
options?: { replace: false }
) => void);
updateEdge: (
id: string,
edgeUpdate: Partial<Edge> | ((edge: Edge) => Partial<Edge>),
options?: { replace: boolean }
) => void;
toObject: () => { nodes: Node[]; edges: Edge[]; viewport: Viewport };
/**
* Returns the bounds of the given nodes or node ids.
+2
View File
@@ -243,6 +243,8 @@ export function createStore(signals: StoreSignals): SvelteFlowStore {
if (node.selected !== selected) {
// we need to mutate the node here in order to have the correct selected state in the drag handler
const internalNode = store.nodeLookup.get(node.id);
if (internalNode) internalNode.selected = selected;
node.selected = selected;
return { ...node };
}
@@ -91,14 +91,13 @@ export const getInitialStore = (signals: StoreSignals) => {
connectionLookup: ConnectionLookup = new Map();
edgeLookup: EdgeLookup = new Map();
adoptNodes: true = $derived.by(() => {
adoptUserNodes(signals.nodes, this.nodeLookup, this.parentLookup, {
adoptNodes: Set<string> = $derived.by(() => {
return adoptUserNodes(signals.nodes, this.nodeLookup, this.parentLookup, {
nodeExtent: this.nodeExtent,
nodeOrigin: this.nodeOrigin,
elevateNodesOnSelect: false,
checkEquality: true
});
return true;
});
adoptEdges: true = $derived.by(() => {
updateConnectionLookup(this.connectionLookup, this.edgeLookup, signals.edges);
@@ -197,8 +196,8 @@ export const getInitialStore = (signals: StoreSignals) => {
edgesInitialized: boolean = $state(false);
viewportInitialized: boolean = $state(false);
initialNodesLength: number = signals.nodes.length;
initialEdgesLength: number = signals.edges.length;
initialNodesLength: number = signals.nodes?.length ?? 0;
initialEdgesLength: number = signals.edges?.length ?? 0;
initialized: boolean = $derived.by(() => {
let initialized = false;
// if it hasn't been initialised check if it's now
+6 -2
View File
@@ -93,6 +93,7 @@ export function adoptUserNodes<NodeType extends NodeBase>(
nodeLookup.clear();
parentLookup.clear();
const updatedChildNodes = new Set<string>();
for (const userNode of nodes) {
let internalNode = tmpLookup.get(userNode.id);
@@ -123,9 +124,10 @@ export function adoptUserNodes<NodeType extends NodeBase>(
}
if (userNode.parentId) {
updateChildNode(internalNode, nodeLookup, parentLookup, options);
updateChildNode(internalNode, nodeLookup, parentLookup, options, updatedChildNodes);
}
}
return updatedChildNodes;
}
function updateParentLookup<NodeType extends NodeBase>(
@@ -152,7 +154,8 @@ function updateChildNode<NodeType extends NodeBase>(
node: InternalNodeBase<NodeType>,
nodeLookup: NodeLookup<InternalNodeBase<NodeType>>,
parentLookup: ParentLookup<InternalNodeBase<NodeType>>,
options?: UpdateNodesOptions<NodeType>
options?: UpdateNodesOptions<NodeType>,
updatedChildNodes?: Set<string>
) {
const { elevateNodesOnSelect, nodeOrigin, nodeExtent } = mergeObjects(defaultOptions, options);
const parentId = node.parentId!;
@@ -178,6 +181,7 @@ function updateChildNode<NodeType extends NodeBase>(
positionAbsolute: positionChanged ? { x, y } : positionAbsolute,
z,
};
updatedChildNodes?.add(node.id);
}
}