- {#each $edgesWithDataStore as edge}
+ {#each $edgesWithDataStore as edge (edge.id)}
{/each}
diff --git a/packages/svelte/src/lib/container/NodeRenderer/index.svelte b/packages/svelte/src/lib/container/NodeRenderer/index.svelte
index 970a362d..4aba3eaf 100644
--- a/packages/svelte/src/lib/container/NodeRenderer/index.svelte
+++ b/packages/svelte/src/lib/container/NodeRenderer/index.svelte
@@ -1,7 +1,7 @@
- {#each $nodesStore as node}
-
+ {#each $nodesStore as node (node.id)}
+
{/each}
diff --git a/packages/svelte/src/lib/container/SvelteFlow.svelte b/packages/svelte/src/lib/container/SvelteFlow.svelte
index 238f0dbb..c27fe786 100644
--- a/packages/svelte/src/lib/container/SvelteFlow.svelte
+++ b/packages/svelte/src/lib/container/SvelteFlow.svelte
@@ -10,16 +10,19 @@
import EdgeRenderer from '$lib/container/EdgeRenderer/index.svelte';
import UserSelection from '$lib/components/UserSelection/index.svelte';
import NodeSelection from '$lib/components/NodeSelection/index.svelte';
- import KeyHandler from '$lib/components/KeyHandler/index.svelte';
+ import { KeyHandler } from '$lib/components/KeyHandler';
import type { SvelteFlowProps } from '$lib/types';
- interface $$Props extends SvelteFlowProps {}
+ type $$Props = SvelteFlowProps;
export let id: $$Props['id'] = '1';
export let nodes: $$Props['nodes'] = [];
export let edges: $$Props['edges'] = [];
export let fitView: $$Props['fitView'] = undefined;
export let nodeTypes: $$Props['nodeTypes'] = undefined;
+ export let selectionKey: $$Props['selectionKey'] = undefined;
+ export let deleteKey: $$Props['deleteKey'] = undefined;
+
let className: $$Props['class'] = undefined;
export { className as class };
@@ -61,7 +64,7 @@
id={id}
bind:this={domNode}
>
-
+
diff --git a/packages/svelte/src/lib/store/index.ts b/packages/svelte/src/lib/store/index.ts
index e417c047..68ceaa35 100644
--- a/packages/svelte/src/lib/store/index.ts
+++ b/packages/svelte/src/lib/store/index.ts
@@ -13,7 +13,7 @@ import {
type SelectionRect,
type Node as RFNode
} from '@reactflow/system';
-import { fitView, getD3Transition, getDimensions } from '@reactflow/utils';
+import { fitView, getConnectedEdges, getD3Transition, getDimensions } from '@reactflow/utils';
import { getHandleBounds } from '../../utils';
import {
@@ -65,6 +65,7 @@ type SvelteFlowStore = {
selectionRectModeStore: Writable;
selectionMode: Writable;
selectionKeyPressedStore: Writable;
+ deleteKeyPressedStore: Writable;
nodeTypesStore: Writable;
edgeTypesStore: Writable;
setNodes: (nodes: Node[]) => void;
@@ -79,6 +80,7 @@ type SvelteFlowStore = {
) => void;
updateNodeDimensions: (updates: NodeDimensionUpdate[]) => void;
resetSelectedElements: () => void;
+ addSelectedNodes: (ids: string[]) => void;
};
export function createStore({
@@ -102,6 +104,8 @@ export function createStore({
const draggingStore = writable(false);
const selectionRectStore = writable(null);
const selectionKeyPressedStore = writable(false);
+ const multiselectionKeyPressedStore = writable(false);
+ const deleteKeyPressedStore = writable(false);
const selectionRectModeStore = writable(null);
const selectionMode = writable(SelectionMode.Partial);
const nodeTypesStore = writable({
@@ -310,6 +314,66 @@ export function createStore({
edgesStore.update((es) => es.map(resetSelectedItem));
}
+ deleteKeyPressedStore.subscribe((deleteKeyPressed) => {
+ if (deleteKeyPressed) {
+ const nodes = get(nodesStore);
+ const edges = get(edgesStore);
+ const selectedNodes = nodes.filter((node) => node.selected);
+ const selectedEdges = edges.filter((edge) => edge.selected);
+
+ // @todo can we put this stuff into @reactflow/utils?
+ const nodeIds = selectedNodes.map((node) => node.id);
+ const edgeIds = selectedEdges.map((edge) => edge.id);
+ const nodesToRemove = nodes.reduce((res, node) => {
+ const parentHit =
+ !nodeIds.includes(node.id) &&
+ node.parentNode &&
+ res.find((n) => n.id === node.parentNode);
+ const deletable = typeof node.deletable === 'boolean' ? node.deletable : true;
+ if (deletable && (nodeIds.includes(node.id) || parentHit)) {
+ res.push(node);
+ }
+
+ return res;
+ }, []);
+ const deletableEdges = edges.filter((e) =>
+ typeof e.deletable === 'boolean' ? e.deletable : true
+ );
+ const initialHitEdges = deletableEdges.filter((e) => edgeIds.includes(e.id));
+
+ if (nodesToRemove || initialHitEdges) {
+ const connectedEdges = getConnectedEdges(nodesToRemove as RFNode[], deletableEdges);
+ const edgesToRemove = [...initialHitEdges, ...connectedEdges];
+ const edgeIdsToRemove = edgesToRemove.reduce((res, edge) => {
+ if (!res.includes(edge.id)) {
+ res.push(edge.id);
+ }
+ return res;
+ }, []);
+
+ nodesStore.update((nds) => nds.filter((node) => !nodeIds.includes(node.id)));
+ edgesStore.update((eds) => eds.filter((edge) => !edgeIdsToRemove.includes(edge.id)));
+ }
+ }
+ });
+
+ function addSelectedNodes(ids: string[]) {
+ selectionRectStore.set(null);
+
+ if (get(multiselectionKeyPressedStore)) {
+ // @todo handle multiselection key
+ }
+
+ nodesStore.update((ns) =>
+ ns.map((node) => {
+ return {
+ ...node,
+ selected: ids.includes(node.id)
+ };
+ })
+ );
+ }
+
return {
nodesStore,
edgesStore,
@@ -323,6 +387,7 @@ export function createStore({
draggingStore,
selectionRectStore,
selectionKeyPressedStore,
+ deleteKeyPressedStore,
selectionRectModeStore,
selectionMode,
nodeTypesStore,
@@ -334,7 +399,8 @@ export function createStore({
zoomIn,
zoomOut,
fitView: _fitView,
- resetSelectedElements
+ resetSelectedElements,
+ addSelectedNodes
};
}
diff --git a/packages/svelte/src/lib/types/edges.ts b/packages/svelte/src/lib/types/edges.ts
index 754aa458..217b0efd 100644
--- a/packages/svelte/src/lib/types/edges.ts
+++ b/packages/svelte/src/lib/types/edges.ts
@@ -9,6 +9,7 @@ export type Edge = {
sourceHandle?: string;
targetHandle?: string;
selected?: boolean;
+ deletable?: boolean;
};
export type EdgeProps = {
diff --git a/packages/svelte/src/lib/types/general.ts b/packages/svelte/src/lib/types/general.ts
index 3218b914..f19e96d1 100644
--- a/packages/svelte/src/lib/types/general.ts
+++ b/packages/svelte/src/lib/types/general.ts
@@ -1,9 +1,17 @@
import type { Node, NodeTypes } from './nodes';
import type { Edge } from './edges';
+import type { ShortcutModifierDefinition } from '@svelte-put/shortcut';
+
+export type KeyModifier = ShortcutModifierDefinition;
+export type KeyDefinitionObject = { key: string; modifier?: KeyModifier };
+export type KeyDefinition = string | KeyDefinitionObject;
export type SvelteFlowProps = {
nodes: Node[];
edges: Edge[];
+
+ selectionKey?: KeyDefinition;
+ deleteKey?: KeyDefinition;
nodeTypes?: NodeTypes;
fitView?: boolean;
class?: string;
diff --git a/packages/svelte/src/lib/types/nodes.ts b/packages/svelte/src/lib/types/nodes.ts
index a359002d..cc180f12 100644
--- a/packages/svelte/src/lib/types/nodes.ts
+++ b/packages/svelte/src/lib/types/nodes.ts
@@ -1,6 +1,8 @@
import type { SvelteComponentTyped } from 'svelte';
import type { internalsSymbol, NodeHandleBounds, Position, XYPosition } from '@reactflow/system';
+// @todo: currently the helper function only like Node from '@reactflow/core'
+// we need a base node type or helpes that accept Node like types
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export type Node = {
id: string;
@@ -16,6 +18,9 @@ export type Node = {
selected?: boolean;
class?: string;
style?: string;
+ deletable?: boolean;
+ // not supported yet
+ parentNode?: string;
// only used internally
[internalsSymbol]?: {