feature(deleteSelectedElements): add deleteSelectedElements method

This commit is contained in:
pengfu
2022-09-17 21:44:14 +08:00
parent f8c39ea822
commit 423f45e0c9
4 changed files with 110 additions and 77 deletions
+6 -1
View File
@@ -56,6 +56,10 @@ const BasicFlow = () => {
);
};
const deleteElements = () => {
instance.deleteSelectedElements()
}
return (
<ReactFlow
defaultNodes={initialNodes}
@@ -82,7 +86,8 @@ const BasicFlow = () => {
<button onClick={toggleClassnames} style={{ marginRight: 5 }}>
toggle classnames
</button>
<button onClick={logToObject}>toObject</button>
<button onClick={logToObject} style={{ marginRight: 5 }}>toObject</button>
<button onClick={deleteElements}>delete selected elements</button>
</div>
</ReactFlow>
);
+7 -74
View File
@@ -1,94 +1,27 @@
import { useEffect } from 'react';
import shallow from 'zustand/shallow';
import { useStore, useStoreApi } from '../store';
import { useStoreApi } from '../store';
import useKeyPress from './useKeyPress';
import { getConnectedEdges } from '../utils/graph';
import { EdgeChange, KeyCode, NodeChange, Node, ReactFlowState } from '../types';
import { KeyCode } from '../types';
import useReactFlow from './useReactFlow';
interface HookParams {
deleteKeyCode: KeyCode | null;
multiSelectionKeyCode: KeyCode | null;
}
const selector = (s: ReactFlowState) => ({
onNodesChange: s.onNodesChange,
onEdgesChange: s.onEdgesChange,
});
export default ({ deleteKeyCode, multiSelectionKeyCode }: HookParams): void => {
const store = useStoreApi();
const { onNodesChange, onEdgesChange } = useStore(selector, shallow);
const { deleteSelectedElements } = useReactFlow();
const deleteKeyPressed = useKeyPress(deleteKeyCode);
const multiSelectionKeyPressed = useKeyPress(multiSelectionKeyCode);
useEffect(() => {
const { nodeInternals, edges, hasDefaultNodes, hasDefaultEdges, onNodesDelete, onEdgesDelete } = store.getState();
const nodes = Array.from(nodeInternals.values());
const nodesToRemove = nodes.reduce<Node[]>((res, node) => {
if (!node.selected && node.parentNode && res.find((n) => n.id === node.parentNode)) {
res.push(node);
} else if (node.selected) {
res.push(node);
}
return res;
}, []);
const selectedEdges = edges.filter((e) => e.selected);
if (deleteKeyPressed && (nodesToRemove || selectedEdges)) {
const connectedEdges = getConnectedEdges(nodesToRemove, edges);
const edgesToRemove = [...selectedEdges, ...connectedEdges];
const edgeIdsToRemove = edgesToRemove.reduce<string[]>((res, edge) => {
if (!res.includes(edge.id)) {
res.push(edge.id);
}
return res;
}, []);
if (hasDefaultEdges || hasDefaultNodes) {
if (hasDefaultEdges) {
store.setState({
edges: edges.filter((e) => !edgeIdsToRemove.includes(e.id)),
});
}
if (hasDefaultNodes) {
nodesToRemove.forEach((node) => {
nodeInternals.delete(node.id);
});
store.setState({
nodeInternals: new Map(nodeInternals),
});
}
}
if (edgeIdsToRemove.length > 0) {
onEdgesDelete?.(edgesToRemove);
if (onEdgesChange) {
const edgeChanges: EdgeChange[] = edgeIdsToRemove.map((id) => ({
id,
type: 'remove',
}));
onEdgesChange(edgeChanges);
}
}
if (nodesToRemove.length > 0) {
onNodesDelete?.(nodesToRemove);
if (onNodesChange) {
const nodeChanges: NodeChange[] = nodesToRemove.map((n) => ({ id: n.id, type: 'remove' }));
onNodesChange(nodeChanges);
}
}
store.setState({ nodesSelectionActive: false });
if (deleteKeyPressed) {
deleteSelectedElements();
}
}, [deleteKeyPressed, onNodesChange, onEdgesChange]);
}, [deleteKeyPressed]);
useEffect(() => {
store.setState({ multiSelectionActive: multiSelectionKeyPressed });
+95 -2
View File
@@ -1,7 +1,7 @@
import { useCallback, useMemo } from 'react';
import useViewportHelper from './useViewportHelper';
import { useStoreApi } from '../store';
import { useStore, useStoreApi } from '../store';
import {
ReactFlowInstance,
Instance,
@@ -11,12 +11,25 @@ import {
EdgeResetChange,
NodeRemoveChange,
EdgeRemoveChange,
ReactFlowState,
EdgeChange,
NodeChange,
Node,
} from '../types';
import { getConnectedEdges } from '../utils/graph';
import shallow from 'zustand/shallow';
const selector = (s: ReactFlowState) => ({
onNodesChange: s.onNodesChange,
onEdgesChange: s.onEdgesChange,
});
export default function useReactFlow<NodeData = any, EdgeData = any>(): ReactFlowInstance<NodeData, EdgeData> {
const viewportHelper = useViewportHelper();
const store = useStoreApi();
const { onNodesChange, onEdgesChange } = useStore(selector, shallow);
const getNodes = useCallback<Instance.GetNodes<NodeData>>(() => {
const { nodeInternals } = store.getState();
const nodes = Array.from(nodeInternals.values());
@@ -110,6 +123,73 @@ export default function useReactFlow<NodeData = any, EdgeData = any>(): ReactFlo
};
}, []);
const deleteSelectedElements = useCallback<Instance.DeleteSelectedElements>(() => {
const { nodeInternals, edges, hasDefaultNodes, hasDefaultEdges, onNodesDelete, onEdgesDelete } = store.getState();
const nodes = Array.from(nodeInternals.values());
const nodesToRemove = nodes.reduce<Node[]>((res, node) => {
if (!node.selected && node.parentNode && res.find((n) => n.id === node.parentNode)) {
res.push(node);
} else if (node.selected) {
res.push(node);
}
return res;
}, []);
const selectedEdges = edges.filter((e) => e.selected);
if (nodesToRemove || selectedEdges) {
const connectedEdges = getConnectedEdges(nodesToRemove, edges);
const edgesToRemove = [...selectedEdges, ...connectedEdges];
const edgeIdsToRemove = edgesToRemove.reduce<string[]>((res, edge) => {
if (!res.includes(edge.id)) {
res.push(edge.id);
}
return res;
}, []);
if (hasDefaultEdges || hasDefaultNodes) {
if (hasDefaultEdges) {
store.setState({
edges: edges.filter((e) => !edgeIdsToRemove.includes(e.id)),
});
}
if (hasDefaultNodes) {
nodesToRemove.forEach((node) => {
nodeInternals.delete(node.id);
});
store.setState({
nodeInternals: new Map(nodeInternals),
});
}
}
if (edgeIdsToRemove.length > 0) {
onEdgesDelete?.(edgesToRemove);
if (onEdgesChange) {
const edgeChanges: EdgeChange[] = edgeIdsToRemove.map((id) => ({
id,
type: 'remove',
}));
onEdgesChange(edgeChanges);
}
}
if (nodesToRemove.length > 0) {
onNodesDelete?.(nodesToRemove);
if (onNodesChange) {
const nodeChanges: NodeChange[] = nodesToRemove.map((n) => ({ id: n.id, type: 'remove' }));
onNodesChange(nodeChanges);
}
}
store.setState({ nodesSelectionActive: false });
}
}, []);
return useMemo(() => {
return {
...viewportHelper,
@@ -122,6 +202,19 @@ export default function useReactFlow<NodeData = any, EdgeData = any>(): ReactFlo
addNodes,
addEdges,
toObject,
deleteSelectedElements,
};
}, [viewportHelper, getNodes, getNode, getEdges, getEdge, setNodes, setEdges, addNodes, addEdges, toObject]);
}, [
viewportHelper,
getNodes,
getNode,
getEdges,
getEdge,
setNodes,
setEdges,
addNodes,
addEdges,
toObject,
deleteSelectedElements,
]);
}
+2
View File
@@ -22,6 +22,7 @@ export namespace Instance {
export type GetEdge<EdgeData> = (id: string) => Edge<EdgeData> | undefined;
export type AddEdges<EdgeData> = (payload: Edge<EdgeData>[] | Edge<EdgeData>) => void;
export type ToObject<NodeData = any, EdgeData = any> = () => ReactFlowJsonObject<NodeData, EdgeData>;
export type DeleteSelectedElements = () => void;
}
export type ReactFlowInstance<NodeData = any, EdgeData = any> = {
@@ -34,5 +35,6 @@ export type ReactFlowInstance<NodeData = any, EdgeData = any> = {
addEdges: Instance.AddEdges<EdgeData>;
getEdge: Instance.GetEdge<EdgeData>;
toObject: Instance.ToObject<NodeData, EdgeData>;
deleteSelectedElements: Instance.DeleteSelectedElements;
viewportInitialized: boolean;
} & Omit<ViewportHelperFunctions, 'initialized'>;