Merge branch 'next' into refactor/tsdoc

This commit is contained in:
moklick
2023-12-19 11:59:44 +01:00
117 changed files with 3388 additions and 2000 deletions
@@ -18,6 +18,7 @@
export let target: $$Props['target'] = '';
export let data: $$Props['data'] = {};
export let style: $$Props['style'] = undefined;
export let zIndex: $$Props['zIndex'] = undefined;
export let animated: $$Props['animated'] = false;
export let selected: $$Props['selected'] = false;
@@ -95,42 +96,44 @@
<!-- svelte-ignore a11y-click-events-have-key-events -->
<!-- svelte-ignore a11y-no-noninteractive-element-interactions -->
{#if !hidden}
<g
class={cc(['svelte-flow__edge', className])}
class:animated
class:selected
data-id={id}
on:click={onClick}
on:contextmenu={onContextMenu}
aria-label={ariaLabel === null
? undefined
: ariaLabel
? ariaLabel
: `Edge from ${source} to ${target}`}
role="img"
>
<svelte:component
this={edgeComponent}
{id}
{source}
{target}
{sourceX}
{sourceY}
{targetX}
{targetY}
{sourcePosition}
{targetPosition}
{animated}
{selected}
{label}
{labelStyle}
{data}
{style}
{interactionWidth}
sourceHandleId={sourceHandle}
targetHandleId={targetHandle}
markerStart={markerStartUrl}
markerEnd={markerEndUrl}
/>
</g>
<svg style:zIndex>
<g
class={cc(['svelte-flow__edge', className])}
class:animated
class:selected
data-id={id}
on:click={onClick}
on:contextmenu={onContextMenu}
aria-label={ariaLabel === null
? undefined
: ariaLabel
? ariaLabel
: `Edge from ${source} to ${target}`}
role="img"
>
<svelte:component
this={edgeComponent}
{id}
{source}
{target}
{sourceX}
{sourceY}
{targetX}
{targetY}
{sourcePosition}
{targetPosition}
{animated}
{selected}
{label}
{labelStyle}
{data}
{style}
{interactionWidth}
sourceHandleId={sourceHandle}
targetHandleId={targetHandle}
markerStart={markerStartUrl}
markerEnd={markerEndUrl}
/>
</g>
</svg>
{/if}
@@ -1,17 +1,18 @@
<script lang="ts">
import { getContext, createEventDispatcher } from 'svelte';
import { getContext } from 'svelte';
import type { Writable } from 'svelte/store';
import cc from 'classcat';
import {
Position,
XYHandle,
isMouseEvent,
type Connection,
type HandleType
areConnectionMapsEqual,
handleConnectionChange
} from '@xyflow/system';
import { useStore } from '$lib/store';
import type { HandleComponentProps } from '$lib/types';
import type { Writable } from 'svelte/store';
type $$Props = HandleComponentProps;
@@ -20,6 +21,8 @@
export let position: $$Props['position'] = Position.Top;
export let style: $$Props['style'] = undefined;
export let isConnectable: $$Props['isConnectable'] = undefined;
export let onconnect: $$Props['onconnect'] = undefined;
export let ondisconnect: $$Props['ondisconnect'] = undefined;
// export let isConnectableStart: $$Props['isConnectableStart'] = undefined;
// export let isConnectableEnd: $$Props['isConnectableEnd'] = undefined;
@@ -32,18 +35,6 @@
$: handleConnectable = isConnectable !== undefined ? isConnectable : $connectable;
const handleId = id || null;
const dispatch = createEventDispatcher<{
connect: { connection: Connection };
connectstart: {
event: MouseEvent | TouchEvent;
nodeId: string | null;
handleId: string | null;
handleType: HandleType | null;
};
connectend: {
event: MouseEvent | TouchEvent;
};
}>();
const store = useStore();
const {
@@ -59,7 +50,12 @@
panBy,
cancelConnection,
updateConnection,
autoPanOnConnect
autoPanOnConnect,
edges,
connectionLookup,
onconnect: onConnectAction,
onconnectstart: onConnectStartAction,
onconnectend: onConnectEndAction
} = store;
function onPointerDown(event: MouseEvent | TouchEvent) {
@@ -88,26 +84,43 @@
}
addEdge(edge);
// @todo: should we change/ improve the stuff we are passing here?
// instead of source/target we could pass fromNodeId, fromHandleId, etc
dispatch('connect', { connection });
$onConnectAction?.(connection);
},
onConnectStart: (event, startParams) => {
dispatch('connectstart', {
event,
$onConnectStartAction?.(event, {
nodeId: startParams.nodeId,
handleId: startParams.handleId,
handleType: startParams.handleType
});
},
onConnectEnd: (event) => {
dispatch('connectend', { event });
$onConnectEndAction?.(event);
},
getTransform: () => [$viewport.x, $viewport.y, $viewport.zoom]
});
}
}
let prevConnections: Map<string, Connection> | null = null;
let connections: Map<string, Connection> | undefined;
$: if (onconnect || ondisconnect) {
// connectionLookup is not reactive, so we use edges to get notified about updates
$edges;
connections = $connectionLookup.get(`${nodeId}-${type}-${id || null}`);
}
$: {
if (prevConnections && !areConnectionMapsEqual(connections, prevConnections)) {
const _connections = connections ?? new Map();
handleConnectionChange(prevConnections, _connections, ondisconnect);
handleConnectionChange(_connections, prevConnections, onconnect);
}
prevConnections = connections ?? new Map();
}
// @todo implement connectablestart, connectableend
</script>
@@ -1,10 +1,10 @@
<script lang="ts">
import { shortcut, type ShortcutModifierDefinition } from '@svelte-put/shortcut';
import { isInputDOMNode, isMacOs } from '@xyflow/system';
import { useStore } from '$lib/store';
import type { KeyHandlerProps } from './types';
import type { KeyDefinition, KeyDefinitionObject } from '$lib/types';
import { isMacOs } from '@xyflow/system';
type $$Props = KeyHandlerProps;
@@ -23,7 +23,7 @@
} = useStore();
function isKeyObject(key?: KeyDefinition | null): key is KeyDefinitionObject {
return typeof key === 'object';
return key !== null && typeof key === 'object';
}
function getModifier(key?: KeyDefinition | null): ShortcutModifierDefinition {
@@ -71,16 +71,19 @@
trigger: [
{
...selectionKeyDefinition,
callback: () => selectionKeyDefinition.key && selectionKeyPressed.set(true)
enabled: selectionKeyDefinition.key !== null,
callback: () => selectionKeyPressed.set(true)
}
],
type: 'keydown'
}}
use:shortcut={{
trigger: [
{
...selectionKeyDefinition,
callback: () => selectionKeyDefinition.key && selectionKeyPressed.set(false)
enabled: selectionKeyDefinition.key !== null,
callback: () => selectionKeyPressed.set(false)
}
],
type: 'keyup'
@@ -89,7 +92,8 @@
trigger: [
{
...multiSelectionKeyDefinition,
callback: () => multiSelectionKeyDefinition.key && multiselectionKeyPressed.set(true)
enabled: multiSelectionKeyDefinition.key !== null,
callback: () => multiselectionKeyPressed.set(true)
}
],
type: 'keydown'
@@ -98,7 +102,8 @@
trigger: [
{
...multiSelectionKeyDefinition,
callback: () => multiSelectionKeyDefinition.key && multiselectionKeyPressed.set(false)
enabled: multiSelectionKeyDefinition.key !== null,
callback: () => multiselectionKeyPressed.set(false)
}
],
type: 'keyup'
@@ -107,7 +112,8 @@
trigger: [
{
...deleteKeyDefinition,
callback: () => deleteKeyDefinition.key && deleteKeyPressed.set(true)
enabled: deleteKeyDefinition.key !== null,
callback: (detail) => !isInputDOMNode(detail.originalEvent) && deleteKeyPressed.set(true)
}
],
type: 'keydown'
@@ -116,7 +122,8 @@
trigger: [
{
...deleteKeyDefinition,
callback: () => deleteKeyDefinition.key && deleteKeyPressed.set(false)
enabled: deleteKeyDefinition.key !== null,
callback: () => deleteKeyPressed.set(false)
}
],
type: 'keyup'
@@ -125,7 +132,8 @@
trigger: [
{
...panActivationKeyDefinition,
callback: () => panActivationKeyDefinition.key && panActivationKeyPressed.set(true)
enabled: panActivationKeyDefinition.key !== null,
callback: () => panActivationKeyPressed.set(true)
}
],
type: 'keydown'
@@ -134,7 +142,8 @@
trigger: [
{
...panActivationKeyDefinition,
callback: () => panActivationKeyDefinition.key && panActivationKeyPressed.set(false)
enabled: panActivationKeyDefinition.key !== null,
callback: () => panActivationKeyPressed.set(false)
}
],
type: 'keyup'
@@ -143,7 +152,8 @@
trigger: [
{
...zoomActivationKeyDefinition,
callback: () => zoomActivationKeyDefinition.key && zoomActivationKeyPressed.set(true)
enabled: zoomActivationKeyDefinition.key !== null,
callback: () => zoomActivationKeyPressed.set(true)
}
],
type: 'keydown'
@@ -152,7 +162,8 @@
trigger: [
{
...zoomActivationKeyDefinition,
callback: () => zoomActivationKeyDefinition.key && zoomActivationKeyPressed.set(false)
enabled: zoomActivationKeyDefinition.key !== null,
callback: () => zoomActivationKeyPressed.set(false)
}
],
type: 'keyup'
@@ -191,12 +191,10 @@
{dragging}
{dragHandle}
isConnectable={connectable}
positionAbsolute={{ x: positionX, y: positionY }}
positionAbsoluteX={positionX}
positionAbsoluteY={positionY}
{width}
{height}
on:connectstart
on:connect
on:connectend
/>
</div>
{/if}
@@ -18,10 +18,8 @@
export let zIndex: $$Props['zIndex'] = undefined;
export let dragging: $$Props['dragging'] = false;
export let dragHandle: $$Props['dragHandle'] = undefined;
export let positionAbsolute: $$Props['positionAbsolute'] = {
x: 0,
y: 0
};
export let positionAbsoluteX: $$Props['positionAbsoluteX'] = 0;
export let positionAbsoluteY: $$Props['positionAbsoluteY'] = 0;
export let isConnectable: $$Props['isConnectable'] = undefined;
// @todo: there must be a better way to do this
@@ -33,10 +31,11 @@
zIndex;
dragging;
dragHandle;
positionAbsolute;
positionAbsoluteX;
positionAbsoluteY;
isConnectable;
</script>
<Handle type="target" position={targetPosition} on:connectstart on:connect on:connectend />
<Handle type="target" position={targetPosition} />
{data?.label}
<Handle type="source" position={sourcePosition} on:connectstart on:connect on:connectend />
<Handle type="source" position={sourcePosition} />
@@ -15,10 +15,8 @@
export let zIndex: $$Props['zIndex'] = undefined;
export let dragging: $$Props['dragging'] = false;
export let dragHandle: $$Props['dragHandle'] = undefined;
export let positionAbsolute: $$Props['positionAbsolute'] = {
x: 0,
y: 0
};
export let positionAbsoluteX: $$Props['positionAbsoluteX'] = 0;
export let positionAbsoluteY: $$Props['positionAbsoluteY'] = 0;
export let isConnectable: $$Props['isConnectable'] = undefined;
// @todo: there must be a better way to do this
@@ -33,6 +31,7 @@
zIndex;
dragging;
dragHandle;
positionAbsolute;
positionAbsoluteX;
positionAbsoluteY;
isConnectable;
</script>
@@ -18,10 +18,8 @@
export let zIndex: $$Props['zIndex'] = undefined;
export let dragging: $$Props['dragging'] = false;
export let dragHandle: $$Props['dragHandle'] = undefined;
export let positionAbsolute: $$Props['positionAbsolute'] = {
x: 0,
y: 0
};
export let positionAbsoluteX: $$Props['positionAbsoluteX'] = 0;
export let positionAbsoluteY: $$Props['positionAbsoluteY'] = 0;
export let isConnectable: $$Props['isConnectable'] = undefined;
// @todo: there must be a better way to do this
@@ -34,9 +32,10 @@
zIndex;
dragging;
dragHandle;
positionAbsolute;
positionAbsoluteX;
positionAbsoluteY;
isConnectable;
</script>
{data?.label}
<Handle type="source" position={sourcePosition} on:connectstart on:connect on:connectend />
<Handle type="source" position={sourcePosition} />
@@ -18,7 +18,8 @@
export let zIndex: $$Props['zIndex'] = undefined;
export let dragging: $$Props['dragging'] = false;
export let dragHandle: $$Props['dragHandle'] = undefined;
export let positionAbsolute: $$Props['positionAbsolute'] = { x: 0, y: 0 };
export let positionAbsoluteX: $$Props['positionAbsoluteX'] = 0;
export let positionAbsoluteY: $$Props['positionAbsoluteY'] = 0;
export let isConnectable: $$Props['isConnectable'] = undefined;
// @todo: there must be a better way to do this
@@ -31,9 +32,10 @@
zIndex;
dragging;
dragHandle;
positionAbsolute;
positionAbsoluteX;
positionAbsoluteY;
isConnectable;
</script>
{data?.label}
<Handle type="target" position={targetPosition} on:connectstart on:connect on:connectend />
<Handle type="target" position={targetPosition} />
@@ -9,7 +9,7 @@
const {
elementsSelectable,
edgeTree,
visibleEdges,
edges: { setDefaultOptions }
} = useStore();
@@ -18,57 +18,47 @@
});
</script>
{#each $edgeTree as group (group.level)}
<svg style="z-index: {group.level}" class="svelte-flow__edges">
{#if group.isMaxLevel} <MarkerDefinition />{/if}
<g>
{#each group.edges as edge (edge.id)}
{@const edgeType = edge.type || 'default'}
{@const selectable = !!(
edge.selectable ||
($elementsSelectable && typeof edge.selectable === 'undefined')
)}
<EdgeWrapper
id={edge.id}
source={edge.source}
target={edge.target}
data={edge.data}
style={edge.style}
animated={edge.animated}
selected={edge.selected}
hidden={edge.hidden}
label={edge.label}
labelStyle={edge.labelStyle}
markerStart={edge.markerStart}
markerEnd={edge.markerEnd}
sourceHandle={edge.sourceHandle}
targetHandle={edge.targetHandle}
sourceX={edge.sourceX}
sourceY={edge.sourceY}
targetX={edge.targetX}
targetY={edge.targetY}
sourcePosition={edge.sourcePosition}
targetPosition={edge.targetPosition}
ariaLabel={edge.ariaLabel}
interactionWidth={edge.interactionWidth}
class={edge.class}
type={edgeType}
{selectable}
on:edgeclick
on:edgecontextmenu
/>
{/each}
</g>
<div class="svelte-flow__edges">
<svg class="svelte-flow__marker">
<MarkerDefinition />
</svg>
{/each}
<style>
.svelte-flow__edges {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
}
</style>
{#each $visibleEdges as edge (edge.id)}
{@const edgeType = edge.type || 'default'}
{@const selectable = !!(
edge.selectable ||
($elementsSelectable && typeof edge.selectable === 'undefined')
)}
<EdgeWrapper
id={edge.id}
source={edge.source}
target={edge.target}
data={edge.data}
style={edge.style}
animated={edge.animated}
selected={edge.selected}
hidden={edge.hidden}
label={edge.label}
labelStyle={edge.labelStyle}
markerStart={edge.markerStart}
markerEnd={edge.markerEnd}
sourceHandle={edge.sourceHandle}
targetHandle={edge.targetHandle}
sourceX={edge.sourceX}
sourceY={edge.sourceY}
targetX={edge.targetX}
targetY={edge.targetY}
sourcePosition={edge.sourcePosition}
targetPosition={edge.targetPosition}
ariaLabel={edge.ariaLabel}
interactionWidth={edge.interactionWidth}
class={edge.class}
type={edgeType}
zIndex={edge.zIndex}
{selectable}
on:edgeclick
on:edgecontextmenu
/>
{/each}
</div>
@@ -83,9 +83,6 @@
on:nodemouseenter
on:nodemousemove
on:nodemouseleave
on:connectstart
on:connect
on:connectend
on:nodedrag
on:nodedragstart
on:nodedragstop
@@ -78,6 +78,9 @@
export let width: $$Props['width'] = undefined;
export let height: $$Props['height'] = undefined;
export let colorMode: $$Props['colorMode'] = 'light';
export let onconnect: $$Props['onconnect'] = undefined;
export let onconnectstart: $$Props['onconnectstart'] = undefined;
export let onconnectend: $$Props['onconnectend'] = undefined;
export let defaultMarkerColor = '#b1b1b7';
@@ -152,7 +155,10 @@
ondelete,
onedgecreate,
connectionMode,
nodeDragThreshold
nodeDragThreshold,
onconnect,
onconnectstart,
onconnectend
};
updateStoreByKeys(store, updatableProps);
@@ -217,9 +223,6 @@
on:nodemouseenter
on:nodemousemove
on:nodemouseleave
on:connectstart
on:connect
on:connectend
on:nodedragstart
on:nodedrag
on:nodedragstop
@@ -15,7 +15,10 @@ import type {
ConnectionMode,
PanelPosition,
ProOptions,
ColorMode
ColorMode,
OnConnect,
OnConnectStart,
OnConnectEnd
} from '@xyflow/system';
import type {
@@ -92,4 +95,8 @@ export type SvelteFlowProps = DOMAttributes<HTMLDivElement> & {
ondelete?: OnDelete;
onedgecreate?: OnEdgeCreate;
onconnect?: OnConnect;
onconnectstart?: OnConnectStart;
onconnectend?: OnConnectEnd;
};
@@ -66,6 +66,9 @@ export type UpdatableStoreProps = {
ondelete?: UnwrapWritable<SvelteFlowStore['ondelete']>;
onedgecreate?: UnwrapWritable<SvelteFlowStore['onedgecreate']>;
nodeDragThreshold?: UnwrapWritable<SvelteFlowStore['nodeDragThreshold']>;
onconnect?: UnwrapWritable<SvelteFlowStore['onconnect']>;
onconnectstart?: UnwrapWritable<SvelteFlowStore['onconnectstart']>;
onconnectend?: UnwrapWritable<SvelteFlowStore['onconnectend']>;
};
export function updateStoreByKeys(store: SvelteFlowStore, keys: UpdatableStoreProps) {
@@ -0,0 +1,30 @@
import { derived } from 'svelte/store';
import { areConnectionMapsEqual, type Connection, type HandleType } from '@xyflow/system';
import { useStore } from '$lib/store';
export type useHandleConnectionsParams = {
nodeId: string;
type: HandleType;
id?: string | null;
};
const initialConnections: Connection[] = [];
export function useHandleConnections({ nodeId, type, id = null }: useHandleConnectionsParams) {
const { edges, connectionLookup } = useStore();
let prevConnections: Map<string, Connection> | undefined = undefined;
return derived(
[edges, connectionLookup],
([, connectionLookup], set) => {
const nextConnections = connectionLookup.get(`${nodeId}-${type}-${id || null}`);
if (!areConnectionMapsEqual(nextConnections, prevConnections)) {
prevConnections = nextConnections;
set(Array.from(prevConnections?.values() || []));
}
},
initialConnections
);
}
@@ -0,0 +1,64 @@
import { derived, type Readable } from 'svelte/store';
import type { Node } from '$lib/types';
import { useStore } from '$lib/store';
function areNodesDataEqual(a: Node['data'][] | null, b: Node['data'][] | null) {
if ((!a && !b) || (!a?.length && !b?.length)) {
true;
}
if (!a || !b || a.length !== b.length) {
return false;
}
for (let i = 0; i < a.length; i++) {
if (a[i] !== b[i]) {
return false;
}
}
return true;
}
export function useNodesData<NodeType extends Node = Node>(
nodeId: string
): Readable<NodeType['data'] | null>;
export function useNodesData<NodeType extends Node = Node>(
nodeIds: string[]
): Readable<NodeType['data'][]>;
export function useNodesData<NodeType extends Node = Node>(
nodeIds: string[],
guard: (node: Node) => node is NodeType
): Readable<NodeType['data'][]>;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function useNodesData(nodeIds: any): any {
const { nodes, nodeLookup } = useStore();
let prevNodesData: (Node['data'] | null)[] | null = null;
return derived([nodes, nodeLookup], ([, nodeLookup], set) => {
let nextNodesData: (Node['data'] | null)[] | null = null;
const nodeIdArray = Array.isArray(nodeIds);
if (!nodeIdArray) {
nextNodesData = [nodeLookup.get(nodeIds)?.data || null];
} else {
const data = [];
for (const nodeId of nodeIds) {
const nodeData = nodeLookup.get(nodeId)?.data;
if (nodeData) {
data.push(nodeData);
}
}
nextNodesData = data;
}
if (!areNodesDataEqual(nextNodesData, prevNodesData)) {
prevNodesData = nextNodesData;
set(nodeIdArray ? nextNodesData : nextNodesData[0]);
}
});
}
+39 -28
View File
@@ -1,7 +1,5 @@
import { get, type Writable } from 'svelte/store';
import {
getIncomersBase,
getOutgoersBase,
getOverlappingArea,
isRectObject,
nodeToRect,
@@ -20,6 +18,7 @@ import {
import { useStore } from '$lib/store';
import type { Edge, FitViewOptions, Node } from '$lib/types';
import { isNode } from '$lib/utils';
export function useSvelteFlow(): {
zoomIn: ZoomInOut;
@@ -48,9 +47,16 @@ export function useSvelteFlow(): {
screenToFlowPosition: (position: XYPosition) => XYPosition;
flowToScreenPosition: (position: XYPosition) => XYPosition;
viewport: Writable<Viewport>;
getConnectedEdges: (id: string | (Node | { id: Node['id'] })[]) => Edge[];
getIncomers: (node: string | Node | { id: Node['id'] }) => Node[];
getOutgoers: (node: string | Node | { id: Node['id'] }) => Node[];
updateNode: (
id: string,
nodeUpdate: Partial<Node> | ((node: Node) => Partial<Node>),
options?: { replace: boolean }
) => void;
updateNodeData: (
id: string,
dataUpdate: object | ((node: Node) => object),
options?: { replace: boolean }
) => void;
toObject: () => { nodes: Node[]; edges: Edge[]; viewport: Viewport };
} {
const {
@@ -84,6 +90,24 @@ export function useSvelteFlow(): {
return [nodeRect, node, isRect];
};
const updateNode = (
id: string,
nodeUpdate: Partial<Node> | ((node: Node) => Partial<Node>),
options: { replace: boolean } = { replace: false }
) => {
nodes.update((nds) =>
nds.map((node) => {
if (node.id === id) {
const nextNode = typeof nodeUpdate === 'function' ? nodeUpdate(node as Node) : nodeUpdate;
return options.replace && isNode(nextNode) ? nextNode : { ...node, ...nextNode };
}
return node;
})
);
};
return {
zoomIn,
zoomOut,
@@ -232,29 +256,6 @@ export function useSvelteFlow(): {
y: rendererPosition.y + domY
};
},
getConnectedEdges: (node) => {
const nodeIds = new Set();
if (typeof node === 'string') {
nodeIds.add(node);
} else if (node.length >= 1) {
node.forEach((n) => {
nodeIds.add(n.id);
});
}
return get(edges).filter((edge) => nodeIds.has(edge.source) || nodeIds.has(edge.target));
},
getIncomers: (node) => {
const _node = typeof node === 'string' ? { id: node } : node;
return getIncomersBase(_node, get(nodes), get(edges));
},
getOutgoers: (node) => {
const _node = typeof node === 'string' ? { id: node } : node;
return getOutgoersBase(_node, get(nodes), get(edges));
},
toObject: () => {
return {
nodes: get(nodes).map((node) => ({
@@ -268,6 +269,16 @@ export function useSvelteFlow(): {
viewport: { ...get(viewport) }
};
},
updateNode,
updateNodeData: (id, dataUpdate, options) => {
updateNode(id, (node) => {
const nextData = typeof dataUpdate === 'function' ? dataUpdate(node) : dataUpdate;
return options?.replace
? { ...node, data: nextData }
: { ...node, data: { ...node.data, ...nextData } };
});
},
viewport
};
}
+2
View File
@@ -27,6 +27,8 @@ export * from '$lib/hooks/useSvelteFlow';
export * from '$lib/hooks/useUpdateNodeInternals';
export * from '$lib/hooks/useConnection';
export * from '$lib/hooks/useNodesEdges';
export * from '$lib/hooks/useHandleConnections';
export * from '$lib/hooks/useNodesData';
// types
export type {
+2 -2
View File
@@ -24,7 +24,7 @@ import type { EdgeTypes, NodeTypes, Node, Edge, FitViewOptions, ConnectionData }
import { initialEdgeTypes, initialNodeTypes, getInitialStore } from './initial-store';
import type { SvelteFlowStore } from './types';
import { syncNodeStores, syncEdgeStores, syncViewportStores } from './utils';
import { getEdgeTree } from './edge-tree';
import { getVisibleEdges } from './visible-edges';
import { getVisibleNodes } from './visible-nodes';
import { getDerivedConnectionProps } from './derived-connection-props';
@@ -357,8 +357,8 @@ export function createStore({
...store,
// derived state
edgeTree: getEdgeTree(store),
connection: getDerivedConnectionProps(store, currentConnection),
visibleEdges: getVisibleEdges(store),
visibleNodes: getVisibleNodes(store),
markers: derived(
[store.edges, store.defaultMarkerColor, store.flowId],
+24 -12
View File
@@ -4,20 +4,25 @@ import {
SelectionMode,
ConnectionMode,
ConnectionLineType,
devWarn,
adoptUserProvidedNodes,
getNodesBounds,
getViewportForBounds,
updateConnectionLookup,
type SelectionRect,
type SnapGrid,
type MarkerProps,
type PanZoomInstance,
type CoordinateExtent,
type IsValidConnection,
type GroupedEdges,
type NodeOrigin,
type OnError,
devWarn,
type Viewport,
updateNodes,
getNodesBounds,
getViewportForBounds
type ConnectionLookup,
type OnConnect,
type OnConnectStart,
type OnConnectEnd,
type NodeLookup
} from '@xyflow/system';
import DefaultNode from '$lib/components/nodes/DefaultNode.svelte';
@@ -72,11 +77,14 @@ export const getInitialStore = ({
height?: number;
fitView?: boolean;
}) => {
const nodeLookup = new Map<string, Node>();
const nextNodes = updateNodes(nodes, nodeLookup, {
const nodeLookup = new Map();
const nextNodes = adoptUserProvidedNodes(nodes, nodeLookup, {
nodeOrigin: [0, 0],
elevateNodesOnSelect: false
});
const connectionLookup = new Map();
const edgeLookup = new Map();
updateConnectionLookup(connectionLookup, edgeLookup, edges);
let viewport: Viewport = { x: 0, y: 0, zoom: 1 };
@@ -89,16 +97,17 @@ export const getInitialStore = ({
return {
flowId: writable<string | null>(null),
nodes: createNodesStore(nextNodes, nodeLookup),
nodeLookup: readable<Map<string, Node>>(nodeLookup),
nodeLookup: readable<NodeLookup>(nodeLookup),
visibleNodes: readable<Node[]>([]),
edges: createEdgesStore(edges),
edgeTree: readable<GroupedEdges<EdgeLayouted>[]>([]),
edges: createEdgesStore(edges, connectionLookup, edgeLookup),
visibleEdges: readable<EdgeLayouted[]>([]),
connectionLookup: readable<ConnectionLookup>(connectionLookup),
height: writable<number>(500),
width: writable<number>(500),
minZoom: writable<number>(0.5),
maxZoom: writable<number>(2),
nodeOrigin: writable<NodeOrigin>([0, 0]),
nodeDragThreshold: writable<number>(0),
nodeDragThreshold: writable<number>(1),
nodeExtent: writable<CoordinateExtent>(infiniteExtent),
translateExtent: writable<CoordinateExtent>(infiniteExtent),
autoPanOnNodeDrag: writable<boolean>(true),
@@ -136,6 +145,9 @@ export const getInitialStore = ({
onlyRenderVisibleElements: writable<boolean>(false),
onerror: writable<OnError>(devWarn),
ondelete: writable<OnDelete>(undefined),
onedgecreate: writable<OnEdgeCreate>(undefined)
onedgecreate: writable<OnEdgeCreate>(undefined),
onconnect: writable<OnConnect>(undefined),
onconnectstart: writable<OnConnectStart>(undefined),
onconnectend: writable<OnConnectEnd>(undefined)
};
};
+18 -5
View File
@@ -1,12 +1,20 @@
import {
writable,
get,
type Unsubscriber,
type Subscriber,
type Updater,
type Writable,
get
type Writable
} from 'svelte/store';
import { updateNodes, type Viewport, type PanZoomInstance } from '@xyflow/system';
import {
adoptUserProvidedNodes,
updateConnectionLookup,
type Viewport,
type PanZoomInstance,
type ConnectionLookup,
type EdgeLookup,
type NodeLookup
} from '@xyflow/system';
import type { DefaultEdgeOptions, DefaultNodeOptions, Edge, Node } from '$lib/types';
@@ -119,7 +127,7 @@ export type NodeStoreOptions = {
// The user only passes in relative positions, so we need to calculate the absolute positions based on the parent nodes.
export const createNodesStore = (
nodes: Node[],
nodeLookup: Map<string, Node>
nodeLookup: NodeLookup<Node>
): {
subscribe: (this: void, run: Subscriber<Node[]>) => Unsubscriber;
update: (this: void, updater: Updater<Node[]>) => void;
@@ -133,7 +141,7 @@ export const createNodesStore = (
let elevateNodesOnSelect = true;
const _set = (nds: Node[]): Node[] => {
const nextNodes = updateNodes(nds, nodeLookup, {
const nextNodes = adoptUserProvidedNodes(nds, nodeLookup, {
elevateNodesOnSelect,
defaults
});
@@ -168,6 +176,8 @@ export const createNodesStore = (
export const createEdgesStore = (
edges: Edge[],
connectionLookup: ConnectionLookup,
edgeLookup: EdgeLookup<Edge>,
defaultOptions?: DefaultEdgeOptions
): Writable<Edge[]> & { setDefaultOptions: (opts: DefaultEdgeOptions) => void } => {
const { subscribe, set, update } = writable<Edge[]>([]);
@@ -176,6 +186,9 @@ export const createEdgesStore = (
const _set: typeof set = (eds: Edge[]) => {
const nextEdges = defaults ? eds.map((edge) => ({ ...defaults, ...edge })) : eds;
updateConnectionLookup(connectionLookup, edgeLookup, nextEdges);
value = nextEdges;
set(value);
};
@@ -1,10 +1,10 @@
import { derived } from 'svelte/store';
import { groupEdgesByZLevel, isEdgeVisible, getEdgePosition } from '@xyflow/system';
import { isEdgeVisible, getEdgePosition, getElevatedEdgeZIndex } from '@xyflow/system';
import type { EdgeLayouted } from '$lib/types';
import type { SvelteFlowStoreState } from './types';
export function getEdgeTree(store: SvelteFlowStoreState) {
export function getVisibleEdges(store: SvelteFlowStoreState) {
const visibleEdges = derived(
[
store.edges,
@@ -64,6 +64,13 @@ export function getEdgeTree(store: SvelteFlowStoreState) {
if (edgePosition) {
res.push({
...edge,
zIndex: getElevatedEdgeZIndex({
selected: edge.selected,
zIndex: edge.zIndex,
sourceNode,
targetNode,
elevateOnSelect: false
}),
...edgePosition
});
}
@@ -71,9 +78,7 @@ export function getEdgeTree(store: SvelteFlowStoreState) {
return res;
}, []);
const groupedEdges = groupEdgesByZLevel<EdgeLayouted>(layoutedEdges, nodeLookup, false);
return groupedEdges;
return layoutedEdges;
}
);
}
+2 -1
View File
@@ -98,7 +98,7 @@ export type StraightEdgeProps = Omit<EdgeComponentProps, 'sourcePosition' | 'tar
export type EdgeTypes = Record<string, ComponentType<SvelteComponent<EdgeProps>>>;
export type DefaultEdgeOptions = Omit<DefaultEdgeOptionsBase<Edge>, 'focusable'>;
export type DefaultEdgeOptions = DefaultEdgeOptionsBase<Edge>;
export type EdgeLayouted = Pick<
Edge,
@@ -121,6 +121,7 @@ export type EdgeLayouted = Pick<
| 'ariaLabel'
| 'hidden'
| 'class'
| 'zIndex'
> &
EdgePosition & {
sourceNode?: Node;
+2
View File
@@ -31,6 +31,8 @@ export type HandleComponentProps = {
isConnectable?: boolean;
isConnectableStart?: boolean;
isConnectableEnd?: boolean;
onconnect?: (connections: Connection[]) => void;
ondisconnect?: (connections: Connection[]) => void;
};
export type FitViewOptions = FitViewOptionsBase<Node>;
+2 -2
View File
@@ -17,7 +17,7 @@ import type { Edge, Node } from '$lib/types';
* @param element - The element to test
* @returns A boolean indicating whether the element is an Node
*/
export const isNode = isNodeBase<Node, Edge>;
export const isNode = isNodeBase<Node>;
/**
* Test whether an object is useable as an Edge
@@ -26,7 +26,7 @@ export const isNode = isNodeBase<Node, Edge>;
* @param element - The element to test
* @returns A boolean indicating whether the element is an Edge
*/
export const isEdge = isEdgeBase<Node, Edge>;
export const isEdge = isEdgeBase<Edge>;
/**
* Pass in a node, and get connected nodes where edge.source === node.id