fixed subflows, cleaned up nodeRenderer, cleaned up nodewrapper
This commit is contained in:
@@ -1,243 +1,8 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {
|
import { SvelteFlowProvider } from '@xyflow/svelte';
|
||||||
SvelteFlow,
|
import Flow from './Flow.svelte';
|
||||||
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];
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<SvelteFlow
|
<SvelteFlowProvider>
|
||||||
bind:nodes
|
<Flow />
|
||||||
bind:edges
|
</SvelteFlowProvider>
|
||||||
{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>
|
|
||||||
|
|||||||
@@ -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">
|
<script lang="ts">
|
||||||
import { writable } from 'svelte/store';
|
|
||||||
import {
|
import {
|
||||||
SvelteFlow,
|
SvelteFlow,
|
||||||
Controls,
|
Controls,
|
||||||
@@ -7,7 +6,8 @@
|
|||||||
BackgroundVariant,
|
BackgroundVariant,
|
||||||
MiniMap,
|
MiniMap,
|
||||||
type NodeTypes,
|
type NodeTypes,
|
||||||
type Node
|
type Node,
|
||||||
|
type Edge
|
||||||
} from '@xyflow/svelte';
|
} from '@xyflow/svelte';
|
||||||
|
|
||||||
import '@xyflow/svelte/dist/style.css';
|
import '@xyflow/svelte/dist/style.css';
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
default: DebugNode
|
default: DebugNode
|
||||||
};
|
};
|
||||||
|
|
||||||
const nodes = writable<Node[]>([
|
let nodes = $state<Node[]>([
|
||||||
{
|
{
|
||||||
id: '1',
|
id: '1',
|
||||||
type: 'input',
|
type: 'input',
|
||||||
@@ -95,7 +95,7 @@
|
|||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const edges = writable([
|
let edges = $state<Edge[]>([
|
||||||
{
|
{
|
||||||
id: 'e1-2',
|
id: 'e1-2',
|
||||||
source: '1',
|
source: '1',
|
||||||
@@ -115,11 +115,9 @@
|
|||||||
{ id: 'e4a-4b2', source: '4a', target: '4b2', zIndex: 100 },
|
{ id: 'e4a-4b2', source: '4a', target: '4b2', zIndex: 100 },
|
||||||
{ id: 'e4b1-4b2', source: '4b1', target: '4b2' }
|
{ id: 'e4b1-4b2', source: '4b1', target: '4b2' }
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$inspect($nodes);
|
|
||||||
</script>
|
</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 />
|
<Controls />
|
||||||
<Background variant={BackgroundVariant.Dots} />
|
<Background variant={BackgroundVariant.Dots} />
|
||||||
<MiniMap />
|
<MiniMap />
|
||||||
|
|||||||
@@ -1,45 +1,18 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { setContext, onDestroy } from 'svelte';
|
import { setContext, onDestroy } from 'svelte';
|
||||||
import cc from 'classcat';
|
import cc from 'classcat';
|
||||||
import { errorMessages, Position } from '@xyflow/system';
|
import { errorMessages, nodeHasDimensions, Position } from '@xyflow/system';
|
||||||
|
|
||||||
import drag from '$lib/actions/drag';
|
import drag from '$lib/actions/drag';
|
||||||
import DefaultNode from '$lib/components/nodes/DefaultNode.svelte';
|
import DefaultNode from '$lib/components/nodes/DefaultNode.svelte';
|
||||||
import type { ConnectableContext, NodeWrapperProps } from './types';
|
import type { ConnectableContext, NodeWrapperProps } from './types';
|
||||||
import { getNodeInlineStyleDimensions } from './utils';
|
import { getNodeInlineStyleDimensions } from './utils';
|
||||||
import type { NodeEvents } from '$lib/types';
|
import type { Node, NodeEvents } from '$lib/types';
|
||||||
|
|
||||||
let {
|
let {
|
||||||
store,
|
store,
|
||||||
node,
|
node,
|
||||||
id,
|
resizeObserver,
|
||||||
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,
|
|
||||||
nodeClickDistance,
|
nodeClickDistance,
|
||||||
onnodeclick,
|
onnodeclick,
|
||||||
onnodedrag,
|
onnodedrag,
|
||||||
@@ -51,6 +24,58 @@
|
|||||||
onnodecontextmenu
|
onnodecontextmenu
|
||||||
}: NodeWrapperProps & NodeEvents = $props();
|
}: 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 nodeRef: HTMLDivElement | null = $state(null);
|
||||||
let prevNodeRef: HTMLDivElement | null = null;
|
let prevNodeRef: HTMLDivElement | null = null;
|
||||||
|
|
||||||
@@ -144,7 +169,7 @@
|
|||||||
store.handleNodeSelection(id);
|
store.handleNodeSelection(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
onnodeclick?.({ node: node.internals.userNode, event });
|
onnodeclick?.({ node, event });
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -5,40 +5,41 @@ export type ConnectableContext = {
|
|||||||
value: boolean;
|
value: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type NodeWrapperProps = Pick<
|
export type NodeWrapperProps = {
|
||||||
Node,
|
// Pick<
|
||||||
| 'id'
|
// Node,
|
||||||
| 'class'
|
// | 'id'
|
||||||
| 'connectable'
|
// | 'class'
|
||||||
| 'data'
|
// | 'connectable'
|
||||||
| 'draggable'
|
// | 'data'
|
||||||
| 'dragging'
|
// | 'draggable'
|
||||||
| 'selected'
|
// | 'dragging'
|
||||||
| 'selectable'
|
// | 'selected'
|
||||||
| 'deletable'
|
// | 'selectable'
|
||||||
| 'style'
|
// | 'deletable'
|
||||||
| 'type'
|
// | 'style'
|
||||||
| 'sourcePosition'
|
// | 'type'
|
||||||
| 'targetPosition'
|
// | 'sourcePosition'
|
||||||
| 'dragHandle'
|
// | 'targetPosition'
|
||||||
| 'hidden'
|
// | 'dragHandle'
|
||||||
| 'width'
|
// | 'hidden'
|
||||||
| 'height'
|
// | 'width'
|
||||||
| 'initialWidth'
|
// | 'height'
|
||||||
| 'initialHeight'
|
// | 'initialWidth'
|
||||||
| 'parentId'
|
// | 'initialHeight'
|
||||||
> & {
|
// | 'parentId'
|
||||||
measuredWidth?: number;
|
// > & {
|
||||||
measuredHeight?: number;
|
// measuredWidth?: number;
|
||||||
type: string;
|
// measuredHeight?: number;
|
||||||
positionX: number;
|
// type: string;
|
||||||
positionY: number;
|
// positionX: number;
|
||||||
'on:nodeclick'?: (event: MouseEvent) => void;
|
// positionY: number;
|
||||||
resizeObserver?: ResizeObserver | null;
|
// 'on:nodeclick'?: (event: MouseEvent) => void;
|
||||||
isParent?: boolean;
|
// isParent?: boolean;
|
||||||
zIndex: number;
|
// zIndex: number;
|
||||||
node: InternalNode;
|
// initialized: boolean;
|
||||||
initialized: boolean;
|
node: Node;
|
||||||
nodeClickDistance?: number;
|
nodeClickDistance?: number;
|
||||||
|
resizeObserver?: ResizeObserver | null;
|
||||||
store: SvelteFlowStore;
|
store: SvelteFlowStore;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { onDestroy } from 'svelte';
|
import { onDestroy } from 'svelte';
|
||||||
import { nodeHasDimensions } from '@xyflow/system';
|
|
||||||
|
|
||||||
import type { Node, NodeEvents } from '$lib/types';
|
|
||||||
import { NodeWrapper } from '$lib/components/NodeWrapper';
|
import { NodeWrapper } from '$lib/components/NodeWrapper';
|
||||||
|
import type { NodeEvents } from '$lib/types';
|
||||||
import type { SvelteFlowStore } from '$lib/store/types';
|
import type { SvelteFlowStore } from '$lib/store/types';
|
||||||
|
|
||||||
let {
|
let {
|
||||||
@@ -19,8 +18,6 @@
|
|||||||
onnodedragstop
|
onnodedragstop
|
||||||
}: { store: SvelteFlowStore; nodeClickDistance?: number } & NodeEvents = $props();
|
}: { store: SvelteFlowStore; nodeClickDistance?: number } & NodeEvents = $props();
|
||||||
|
|
||||||
// const { nodes } = store;
|
|
||||||
|
|
||||||
const resizeObserver: ResizeObserver | null =
|
const resizeObserver: ResizeObserver | null =
|
||||||
typeof ResizeObserver === 'undefined'
|
typeof ResizeObserver === 'undefined'
|
||||||
? null
|
? null
|
||||||
@@ -43,59 +40,13 @@
|
|||||||
onDestroy(() => {
|
onDestroy(() => {
|
||||||
resizeObserver?.disconnect();
|
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>
|
</script>
|
||||||
|
|
||||||
<div class="svelte-flow__nodes">
|
<div class="svelte-flow__nodes">
|
||||||
{#each store.nodes as node (node.id)}
|
{#each store.nodes as node (node.id)}
|
||||||
{@const internalNode = store.nodeLookup.get(node.id)}
|
|
||||||
<NodeWrapper
|
<NodeWrapper
|
||||||
{store}
|
{store}
|
||||||
node={internalNode!}
|
{node}
|
||||||
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}
|
|
||||||
{resizeObserver}
|
{resizeObserver}
|
||||||
{nodeClickDistance}
|
{nodeClickDistance}
|
||||||
{onnodeclick}
|
{onnodeclick}
|
||||||
|
|||||||
@@ -211,13 +211,11 @@ export function useSvelteFlow(): {
|
|||||||
// nodeUpdate: Partial<Node> | ((node: Node) => Partial<Node>),
|
// nodeUpdate: Partial<Node> | ((node: Node) => Partial<Node>),
|
||||||
// options?: { replace: boolean }
|
// options?: { replace: boolean }
|
||||||
// ) => void;
|
// ) => void;
|
||||||
updateNode:
|
updateNode: (
|
||||||
| ((id: string, nodeUpdate: Node | ((node: Node) => Node), options: { replace: true }) => void)
|
id: string,
|
||||||
| ((
|
nodeUpdate: Partial<Node> | ((node: Node) => Partial<Node>),
|
||||||
id: string,
|
options?: { replace: boolean }
|
||||||
nodeUpdate: Partial<Node> | ((node: Node) => Partial<Node>),
|
) => void;
|
||||||
options: { replace: false }
|
|
||||||
) => void);
|
|
||||||
/**
|
/**
|
||||||
* Updates the data attribute of a node.
|
* Updates the data attribute of a node.
|
||||||
*
|
*
|
||||||
@@ -248,13 +246,11 @@ export function useSvelteFlow(): {
|
|||||||
* @example
|
* @example
|
||||||
* updateNode('node-1', (node) => ({ position: { x: node.position.x + 10, y: node.position.y } }));
|
* updateNode('node-1', (node) => ({ position: { x: node.position.x + 10, y: node.position.y } }));
|
||||||
*/
|
*/
|
||||||
updateEdge:
|
updateEdge: (
|
||||||
| ((id: string, edgeUpdate: Edge | ((edge: Edge) => Edge), options: { replace: true }) => void)
|
id: string,
|
||||||
| ((
|
edgeUpdate: Partial<Edge> | ((edge: Edge) => Partial<Edge>),
|
||||||
id: string,
|
options?: { replace: boolean }
|
||||||
edgeUpdate: Partial<Edge> | ((edge: Edge) => Partial<Edge>),
|
) => void;
|
||||||
options?: { replace: false }
|
|
||||||
) => void);
|
|
||||||
toObject: () => { nodes: Node[]; edges: Edge[]; viewport: Viewport };
|
toObject: () => { nodes: Node[]; edges: Edge[]; viewport: Viewport };
|
||||||
/**
|
/**
|
||||||
* Returns the bounds of the given nodes or node ids.
|
* Returns the bounds of the given nodes or node ids.
|
||||||
|
|||||||
@@ -243,6 +243,8 @@ export function createStore(signals: StoreSignals): SvelteFlowStore {
|
|||||||
|
|
||||||
if (node.selected !== selected) {
|
if (node.selected !== selected) {
|
||||||
// we need to mutate the node here in order to have the correct selected state in the drag handler
|
// 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;
|
node.selected = selected;
|
||||||
return { ...node };
|
return { ...node };
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -91,14 +91,13 @@ export const getInitialStore = (signals: StoreSignals) => {
|
|||||||
connectionLookup: ConnectionLookup = new Map();
|
connectionLookup: ConnectionLookup = new Map();
|
||||||
edgeLookup: EdgeLookup = new Map();
|
edgeLookup: EdgeLookup = new Map();
|
||||||
|
|
||||||
adoptNodes: true = $derived.by(() => {
|
adoptNodes: Set<string> = $derived.by(() => {
|
||||||
adoptUserNodes(signals.nodes, this.nodeLookup, this.parentLookup, {
|
return adoptUserNodes(signals.nodes, this.nodeLookup, this.parentLookup, {
|
||||||
nodeExtent: this.nodeExtent,
|
nodeExtent: this.nodeExtent,
|
||||||
nodeOrigin: this.nodeOrigin,
|
nodeOrigin: this.nodeOrigin,
|
||||||
elevateNodesOnSelect: false,
|
elevateNodesOnSelect: false,
|
||||||
checkEquality: true
|
checkEquality: true
|
||||||
});
|
});
|
||||||
return true;
|
|
||||||
});
|
});
|
||||||
adoptEdges: true = $derived.by(() => {
|
adoptEdges: true = $derived.by(() => {
|
||||||
updateConnectionLookup(this.connectionLookup, this.edgeLookup, signals.edges);
|
updateConnectionLookup(this.connectionLookup, this.edgeLookup, signals.edges);
|
||||||
@@ -197,8 +196,8 @@ export const getInitialStore = (signals: StoreSignals) => {
|
|||||||
edgesInitialized: boolean = $state(false);
|
edgesInitialized: boolean = $state(false);
|
||||||
viewportInitialized: boolean = $state(false);
|
viewportInitialized: boolean = $state(false);
|
||||||
|
|
||||||
initialNodesLength: number = signals.nodes.length;
|
initialNodesLength: number = signals.nodes?.length ?? 0;
|
||||||
initialEdgesLength: number = signals.edges.length;
|
initialEdgesLength: number = signals.edges?.length ?? 0;
|
||||||
initialized: boolean = $derived.by(() => {
|
initialized: boolean = $derived.by(() => {
|
||||||
let initialized = false;
|
let initialized = false;
|
||||||
// if it hasn't been initialised check if it's now
|
// if it hasn't been initialised check if it's now
|
||||||
|
|||||||
@@ -93,6 +93,7 @@ export function adoptUserNodes<NodeType extends NodeBase>(
|
|||||||
nodeLookup.clear();
|
nodeLookup.clear();
|
||||||
parentLookup.clear();
|
parentLookup.clear();
|
||||||
|
|
||||||
|
const updatedChildNodes = new Set<string>();
|
||||||
for (const userNode of nodes) {
|
for (const userNode of nodes) {
|
||||||
let internalNode = tmpLookup.get(userNode.id);
|
let internalNode = tmpLookup.get(userNode.id);
|
||||||
|
|
||||||
@@ -123,9 +124,10 @@ export function adoptUserNodes<NodeType extends NodeBase>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (userNode.parentId) {
|
if (userNode.parentId) {
|
||||||
updateChildNode(internalNode, nodeLookup, parentLookup, options);
|
updateChildNode(internalNode, nodeLookup, parentLookup, options, updatedChildNodes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return updatedChildNodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateParentLookup<NodeType extends NodeBase>(
|
function updateParentLookup<NodeType extends NodeBase>(
|
||||||
@@ -152,7 +154,8 @@ function updateChildNode<NodeType extends NodeBase>(
|
|||||||
node: InternalNodeBase<NodeType>,
|
node: InternalNodeBase<NodeType>,
|
||||||
nodeLookup: NodeLookup<InternalNodeBase<NodeType>>,
|
nodeLookup: NodeLookup<InternalNodeBase<NodeType>>,
|
||||||
parentLookup: ParentLookup<InternalNodeBase<NodeType>>,
|
parentLookup: ParentLookup<InternalNodeBase<NodeType>>,
|
||||||
options?: UpdateNodesOptions<NodeType>
|
options?: UpdateNodesOptions<NodeType>,
|
||||||
|
updatedChildNodes?: Set<string>
|
||||||
) {
|
) {
|
||||||
const { elevateNodesOnSelect, nodeOrigin, nodeExtent } = mergeObjects(defaultOptions, options);
|
const { elevateNodesOnSelect, nodeOrigin, nodeExtent } = mergeObjects(defaultOptions, options);
|
||||||
const parentId = node.parentId!;
|
const parentId = node.parentId!;
|
||||||
@@ -178,6 +181,7 @@ function updateChildNode<NodeType extends NodeBase>(
|
|||||||
positionAbsolute: positionChanged ? { x, y } : positionAbsolute,
|
positionAbsolute: positionChanged ? { x, y } : positionAbsolute,
|
||||||
z,
|
z,
|
||||||
};
|
};
|
||||||
|
updatedChildNodes?.add(node.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user