merge useNodeConnections

This commit is contained in:
peterkogo
2025-01-08 12:59:17 +01:00
27 changed files with 273 additions and 107 deletions
@@ -92,7 +92,7 @@
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
store.edges;
if (onconnect || ondisconnect) {
let connections = store.connectionLookup.get(`${nodeId}-${type}-${handleId}`);
let connections = store.connectionLookup.get(`${nodeId}-${type}${id ? `-${id}` : ''}`);
if (prevConnections && !areConnectionMapsEqual(connections, prevConnections)) {
const _connections = connections ?? new Map();
@@ -15,7 +15,7 @@
return (item: Item) => {
const isSelected = ids.has(item.id);
if (item.selected !== isSelected) {
if (!!item.selected !== isSelected) {
return { ...item, selected: isSelected };
}
@@ -23,8 +23,7 @@
};
}
// TODO: maybe replace with set.intersection?
function setEq(a: Set<string>, b: Set<string>) {
function isSetEqual(a: Set<string>, b: Set<string>) {
if (a.size !== b.size) {
return false;
}
@@ -57,7 +56,6 @@
// svelte-ignore non_reactive_update
let container: HTMLDivElement;
let containerBounds: DOMRect | null = null;
// let selectedNodes: InternalNode[] = [];
let selectedNodeIds: Set<string> = new Set();
let selectedEdgeIds: Set<string> = new Set();
@@ -149,25 +147,27 @@
).map((n) => n.id)
);
// TODO: replace with extended connectionLookup
let edgesSelectable = store.defaultEdgeOptions.selectable ?? true;
selectedEdgeIds = new Set();
store.edges.forEach((edge) => {
if (
selectedNodeIds.has(edge.source) &&
selectedNodeIds.has(edge.target) &&
(edge.selectable ?? edgesSelectable)
) {
selectedEdgeIds.add(edge.id);
// We look for all edges connected to the selected nodes
for (let nodeId of selectedNodeIds) {
let connections = store.connectionLookup.get(nodeId);
if (!connections) continue;
for (let { edgeId } of connections.values()) {
let edge = store.edgeLookup.get(edgeId);
if (edge && (edge.selectable ?? edgesSelectable)) {
selectedEdgeIds.add(edgeId);
}
}
});
}
// this prevents unnecessary updates while updating the selection rectangle
if (setEq(prevSelectedNodeIds, selectedNodeIds)) {
if (isSetEqual(prevSelectedNodeIds, selectedNodeIds)) {
store.nodes = store.nodes.map(toggleSelected(selectedNodeIds));
}
if (setEq(prevSelectedEdgeIds, selectedEdgeIds)) {
if (isSetEqual(prevSelectedEdgeIds, selectedEdgeIds)) {
store.edges = store.edges.map(toggleSelected(selectedEdgeIds));
}
@@ -9,7 +9,7 @@ import { getContext } from 'svelte';
* @param functionName - The name of the function that is being called
* @param force - If true, the warning will be shown regardless if child of <SvelteFlowFlow />
*/
export function derivedWarning(functionName: string, force?: boolean) {
export function derivedWarning(functionName: string) {
const storeContext = getContext<StoreContext>(key);
if (!storeContext) {
@@ -18,7 +18,7 @@ export function derivedWarning(functionName: string, force?: boolean) {
);
}
if ((force || storeContext.provider) && !$effect.tracking()) {
if (storeContext.provider && typeof window === 'object' && !$effect.tracking()) {
console.warn(`Use $derived(${functionName}()) to receive updates when values change.`);
console.trace(functionName);
}
@@ -1,52 +0,0 @@
import { areConnectionMapsEqual, type HandleConnection, type HandleType } from '@xyflow/system';
import { useStore } from '$lib/store';
import { getContext } from 'svelte';
export type useHandleConnectionsParams = {
type: HandleType;
nodeId?: string;
id?: string | null;
};
const initialConnections: HandleConnection[] = [];
/**
* Hook to check if a <Handle /> is connected to another <Handle /> and get the connections.
*
* @public
* @param param.nodeId
* @param param.type - handle type 'source' or 'target'
* @param param.id - the handle id (this is only needed if the node has multiple handles of the same type)
* @returns an array with connections
*/
export function useHandleConnections({
type,
nodeId: _nodeId,
id = null
}: useHandleConnectionsParams) {
const { edges, connectionLookup } = $derived(useStore());
const contextNodeId = getContext<string>('svelteflow__node_id');
const nodeId = _nodeId ?? contextNodeId;
let prevConnections: Map<string, HandleConnection> | undefined = new Map();
let connectionsArray: HandleConnection[] = initialConnections;
const connections = $derived.by(() => {
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
edges;
const nextConnections = connectionLookup.get(`${nodeId}-${type}-${id || null}`);
if (!areConnectionMapsEqual(nextConnections, prevConnections)) {
prevConnections = nextConnections;
connectionsArray = Array.from(nextConnections?.values() || initialConnections);
}
return connectionsArray;
});
return {
get current() {
return connections;
}
};
}
@@ -0,0 +1,59 @@
import { areConnectionMapsEqual, type NodeConnection, type HandleType } from '@xyflow/system';
import { useStore } from '$lib/store';
import { getContext } from 'svelte';
type UseNodeConnectionsParams = {
type?: HandleType;
handleId?: string;
nodeId?: string;
// TODO: Svelte 5
// onConnect?: (connections: Connection[]) => void;
// onDisconnect?: (connections: Connection[]) => void;
};
const initialConnections: NodeConnection[] = [];
/**
* Hook to retrieve all edges connected to a node. Can be filtered by handle type and id.
*
* @public
* @param param.nodeId - node id - optional if called inside a custom node
* @param param.type - filter by handle type 'source' or 'target'
* @param param.handleId - filter by handle id (this is only needed if the node has multiple handles of the same type)
* @todo @param param.onConnect - gets called when a connection is established
* @todo @param param.onDisconnect - gets called when a connection is removed
* @returns an array with connections
*/
export function useNodeConnections({
type,
nodeId: _nodeId,
handleId
}: UseNodeConnectionsParams = {}) {
const { edges, connectionLookup } = $derived(useStore());
const contextNodeId = getContext<string>('svelteflow__node_id');
const nodeId = _nodeId ?? contextNodeId;
let prevConnections: Map<string, NodeConnection> | undefined = new Map();
let connectionsArray: NodeConnection[] = initialConnections;
const connections = $derived.by(() => {
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
edges;
const nextConnections = connectionLookup.get(
`${nodeId}-${type}${handleId ? `-${handleId}` : ''}`
);
if (!areConnectionMapsEqual(nextConnections, prevConnections)) {
prevConnections = nextConnections;
connectionsArray = Array.from(nextConnections?.values() || initialConnections);
}
return connectionsArray;
});
return {
get current() {
return connections;
}
};
}
+1 -1
View File
@@ -35,7 +35,7 @@ export * from '$lib/hooks/useSvelteFlow.svelte';
export * from '$lib/hooks/useUpdateNodeInternals.svelte';
export * from '$lib/hooks/useConnection.svelte';
export * from '$lib/hooks/useNodesEdgesViewport.svelte';
export * from '$lib/hooks/useHandleConnections.svelte';
export * from '$lib/hooks/useNodeConnections.svelte';
export * from '$lib/hooks/useNodesData.svelte';
export * from '$lib/hooks/useInternalNode.svelte';
export { useInitialized, useNodesInitialized } from '$lib/hooks/useInitialized.svelte';