Merge pull request #2536 from wbkd/pengfu-v11_deleteSelectedElements
Feat: add delete elements helper function
This commit is contained in:
@@ -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]);
|
||||
|
||||
@@ -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,
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -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'>;
|
||||
|
||||
Reference in New Issue
Block a user