Merge branch 'v11_deleteSelectedElements' of github.com:pengfu/react-flow into pengfu-v11_deleteSelectedElements

This commit is contained in:
moklick
2022-11-02 15:49:37 +01:00
5 changed files with 125 additions and 82 deletions
@@ -106,7 +106,7 @@ const BasicFlow = () => {
<button onClick={toggleClassnames} style={{ marginRight: 5 }}>
toggle classnames
</button>
<button onClick={logToObject}>toObject</button>
<button onClick={logToObject} style={{ marginRight: 5 }}>toObject</button>
</div>
</ReactFlow>
);
@@ -43,6 +43,7 @@ const initialNodes: Node[] = [
const initialEdges: Edge[] = [
{ id: 'e1-2', source: '1', target: '2', animated: true },
{ id: 'e1-3', source: '1', target: '3' },
// { id: 'e3-4', source: '3', target: '4' }
];
let id = 5;
@@ -64,6 +65,7 @@ const UseZoomPanHelperFlow = () => {
addEdges,
getNodes,
getEdges,
deleteElements
} = useReactFlow();
const onPaneClick = useCallback(
@@ -111,6 +113,16 @@ const UseZoomPanHelperFlow = () => {
console.log('edges', getEdges());
}, [getNodes, getEdges]);
const deleteSelectedElements = useCallback(() => {
const selectedNodes = nodes.filter(node => node.selected);
const selectedEdges = edges.filter(edge => edge.selected);
deleteElements({ nodes: selectedNodes, edges: selectedEdges });
}, [deleteElements, nodes, edges])
const deleteSomeElements = useCallback(() => {
deleteElements({ nodes: [{ id: '2' }], edges: [{ id: 'e1-3' }] })
}, [])
useEffect(() => {
addEdges({ id: 'e3-4', source: '3', target: '4' });
}, [addEdges]);
@@ -137,6 +149,8 @@ const UseZoomPanHelperFlow = () => {
<button onClick={onAddNode}>add node</button>
<button onClick={onResetNodes}>reset nodes</button>
<button onClick={logNodes}>useNodes</button>
<button onClick={deleteSelectedElements}>deleteSelectedElements</button>
<button onClick={deleteSomeElements}>deleteSomeElements</button>
</div>
<Background />
<MiniMap />
+10 -80
View File
@@ -2,9 +2,8 @@ import { useEffect } from 'react';
import { useStoreApi } from '../hooks/useStore';
import useKeyPress from './useKeyPress';
import { getConnectedEdges } from '../utils/graph';
import type { KeyCode, NodeChange, Node } from '../types';
import type { KeyCode } from '../types';
import useReactFlow from './useReactFlow';
interface HookParams {
deleteKeyCode: KeyCode | null;
multiSelectionKeyCode: KeyCode | null;
@@ -12,87 +11,18 @@ interface HookParams {
export default ({ deleteKeyCode, multiSelectionKeyCode }: HookParams): void => {
const store = useStoreApi();
const { deleteElements } = useReactFlow();
const deleteKeyPressed = useKeyPress(deleteKeyCode);
const multiSelectionKeyPressed = useKeyPress(multiSelectionKeyCode);
useEffect(() => {
if (!deleteKeyPressed) {
return;
}
const {
nodeInternals,
edges,
hasDefaultNodes,
hasDefaultEdges,
onNodesDelete,
onEdgesDelete,
onNodesChange,
onEdgesChange,
} = store.getState();
const nodes = Array.from(nodeInternals.values());
const nodesToRemove = nodes.reduce<Node[]>((res, node) => {
const parentSelected = !node.selected && node.parentNode && res.find((n) => n.id === node.parentNode);
const deletable = typeof node.deletable === 'boolean' ? node.deletable : true;
if (deletable && (node.selected || parentSelected)) {
res.push(node);
}
return res;
}, []);
const deletableEdges = edges.filter((e) => (typeof e.deletable === 'boolean' ? e.deletable : true));
const selectedEdges = deletableEdges.filter((e) => e.selected);
if (nodesToRemove || selectedEdges) {
const connectedEdges = getConnectedEdges(nodesToRemove, deletableEdges);
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) {
onEdgesChange(
edgeIdsToRemove.map((id) => ({
id,
type: 'remove',
}))
);
}
}
if (nodesToRemove.length > 0) {
onNodesDelete?.(nodesToRemove);
if (onNodesChange) {
const nodeChanges: NodeChange[] = nodesToRemove.map((n) => ({ id: n.id, type: 'remove' }));
onNodesChange(nodeChanges);
}
}
if (deleteKeyPressed) {
const { nodeInternals, edges } = store.getState();
const nodes = Array.from(nodeInternals.values());
const selectedNodes = nodes.filter((node) => node.selected);
const selectedEdges = edges.filter((edge) => edge.selected);
deleteElements({nodes: selectedNodes, edges: selectedEdges});
store.setState({ nodesSelectionActive: false });
}
}, [deleteKeyPressed]);
+94 -1
View File
@@ -11,7 +11,10 @@ import type {
EdgeResetChange,
NodeRemoveChange,
EdgeRemoveChange,
NodeChange,
Node,
} from '../types';
import { getConnectedEdges } from '../utils/graph';
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
export default function useReactFlow<NodeData = any, EdgeData = any>(): ReactFlowInstance<NodeData, EdgeData> {
@@ -111,6 +114,83 @@ export default function useReactFlow<NodeData = any, EdgeData = any>(): ReactFlo
};
}, []);
const deleteElements = useCallback<Instance.DeleteElements>(({ nodes: nodesDeleted, edges: edgesDeleted }) => {
const {
nodeInternals,
edges,
hasDefaultNodes,
hasDefaultEdges,
onNodesDelete,
onEdgesDelete,
onNodesChange,
onEdgesChange,
} = store.getState();
const nodes = Array.from(nodeInternals.values());
const nodeIds = (nodesDeleted || []).map((node) => node.id);
const edgeIds = (edgesDeleted || []).map((edge) => edge.id);
const nodesToRemove = nodes.reduce<Node[]>((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, deletableEdges);
const edgesToRemove = [...initialHitEdges, ...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) {
onEdgesChange(
edgeIdsToRemove.map((id) => ({
id,
type: 'remove',
}))
);
}
}
if (nodesToRemove.length > 0) {
onNodesDelete?.(nodesToRemove);
if (onNodesChange) {
const nodeChanges: NodeChange[] = nodesToRemove.map((n) => ({ id: n.id, type: 'remove' }));
onNodesChange(nodeChanges);
}
}
}
}, []);
return useMemo(() => {
return {
...viewportHelper,
@@ -123,6 +203,19 @@ export default function useReactFlow<NodeData = any, EdgeData = any>(): ReactFlo
addNodes,
addEdges,
toObject,
deleteElements,
};
}, [viewportHelper, getNodes, getNode, getEdges, getEdge, setNodes, setEdges, addNodes, addEdges, toObject]);
}, [
viewportHelper,
getNodes,
getNode,
getEdges,
getEdge,
setNodes,
setEdges,
addNodes,
addEdges,
toObject,
deleteElements,
]);
}
+6
View File
@@ -8,6 +8,10 @@ export type ReactFlowJsonObject<NodeData = any, EdgeData = any> = {
viewport: Viewport;
};
export type DeleteElementsOptions = {
nodes?: (Partial<Node> & { id: Node['id'] })[],
edges?: (Partial<Edge> & { id: Edge['id'] })[]
};
export namespace Instance {
export type GetNodes<NodeData> = () => Node<NodeData>[];
export type SetNodes<NodeData> = (
@@ -22,6 +26,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 DeleteElements = ({ nodes, edges }: DeleteElementsOptions) => void;
}
export type ReactFlowInstance<NodeData = any, EdgeData = any> = {
@@ -34,5 +39,6 @@ export type ReactFlowInstance<NodeData = any, EdgeData = any> = {
addEdges: Instance.AddEdges<EdgeData>;
getEdge: Instance.GetEdge<EdgeData>;
toObject: Instance.ToObject<NodeData, EdgeData>;
deleteElements: Instance.DeleteElements;
viewportInitialized: boolean;
} & Omit<ViewportHelperFunctions, 'initialized'>;