feat(keyhandler): add onNodeRemove callback
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
import { useEffect, useContext } from 'react';
|
||||
|
||||
import { useKeyPress } from '../hooks';
|
||||
import { setNodesSelection } from '../state/actions';
|
||||
import { GraphContext } from '../GraphContext';
|
||||
|
||||
export default (props) => {
|
||||
const { state, dispatch } = useContext(GraphContext);
|
||||
const removePressed = useKeyPress('Backspace');
|
||||
|
||||
useEffect(() => {
|
||||
if (removePressed && state.selectedNodeIds.length) {
|
||||
props.onNodeRemove(state.selectedNodeIds);
|
||||
dispatch(setNodesSelection({ isActive: false }));
|
||||
}
|
||||
}, [removePressed])
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { createContext, useEffect, useReducer } from 'react';
|
||||
import React, { createContext, useEffect, useReducer, useRef } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import isEqual from 'lodash.isequal';
|
||||
|
||||
@@ -7,6 +7,16 @@ import { setNodes, setEdges } from '../state/actions';
|
||||
|
||||
export const GraphContext = createContext({});
|
||||
|
||||
function usePrevious(value) {
|
||||
const ref = useRef();
|
||||
|
||||
useEffect(() => {
|
||||
ref.current = value;
|
||||
});
|
||||
|
||||
return ref.current;
|
||||
}
|
||||
|
||||
export const Provider = (props) => {
|
||||
const {
|
||||
onNodeClick,
|
||||
|
||||
@@ -3,20 +3,20 @@ import ReactDraggable from 'react-draggable';
|
||||
import cx from 'classnames';
|
||||
|
||||
import { GraphContext } from '../../GraphContext';
|
||||
import { updateNodeData, updateNodePos } from '../../state/actions';
|
||||
import { updateNodeData, updateNodePos, setSelectedNodesIds } from '../../state/actions';
|
||||
|
||||
const isInputTarget = (e) => ['INPUT', 'SELECT', 'TEXTAREA'].includes(e.target.nodeName);
|
||||
|
||||
export default NodeComponent => (props) => {
|
||||
const nodeElement = useRef(null);
|
||||
const graphContext = useContext(GraphContext);
|
||||
const { state, dispatch } = useContext(GraphContext);
|
||||
const [offset, setOffset] = useState({ x: 0, y: 0 });
|
||||
|
||||
const { data, onNodeClick, __rg } = props;
|
||||
const { position } = __rg;
|
||||
const { id } = data;
|
||||
const [ x, y, k ] = graphContext.state.transform;
|
||||
const selected = graphContext.state.selectedNodeIds.includes(id);
|
||||
const [ x, y, k ] = state.transform;
|
||||
const selected = state.selectedNodeIds.includes(id);
|
||||
const nodeClasses = cx('react-graph__node', { selected });
|
||||
|
||||
useEffect(() => {
|
||||
@@ -24,7 +24,7 @@ export default NodeComponent => (props) => {
|
||||
const unscaledWith = Math.round(bounds.width * (1 / k));
|
||||
const unscaledHeight = Math.round(bounds.height * (1 / k));
|
||||
|
||||
graphContext.dispatch(updateNodeData(id, { width: unscaledWith, height: unscaledHeight }));
|
||||
dispatch(updateNodeData(id, { width: unscaledWith, height: unscaledHeight }));
|
||||
}, []);
|
||||
|
||||
return (
|
||||
@@ -50,7 +50,7 @@ export default NodeComponent => (props) => {
|
||||
y: e.clientY * (1 / k)
|
||||
}
|
||||
|
||||
graphContext.dispatch(updateNodePos(id, {
|
||||
dispatch(updateNodePos(id, {
|
||||
x: unscaledPos.x - x - offset.x,
|
||||
y: unscaledPos.y - y - offset.y
|
||||
}));
|
||||
@@ -65,6 +65,8 @@ export default NodeComponent => (props) => {
|
||||
if (isInputTarget(e)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
dispatch(setSelectedNodesIds(id));
|
||||
onNodeClick({ data, position });
|
||||
}}
|
||||
>
|
||||
|
||||
+7
-2
@@ -2,6 +2,7 @@ import React, { PureComponent } from 'react';
|
||||
|
||||
import { parseElements, separateElements } from './graph-utils';
|
||||
import GraphView from './GraphView';
|
||||
import GlobalKeyHandler from './GlobalKeyHandler';
|
||||
import { Provider } from './GraphContext';
|
||||
import { createNodeTypes } from './NodeRenderer/utils';
|
||||
|
||||
@@ -20,7 +21,7 @@ class ReactGraph extends PureComponent {
|
||||
|
||||
render() {
|
||||
const {
|
||||
style, onNodeClick, children, onLoad, onMove, onChange, elements
|
||||
style, onNodeClick, children, onLoad, onMove, onChange, elements, onNodeRemove
|
||||
} = this.props;
|
||||
|
||||
const { nodes, edges } = elements
|
||||
@@ -36,6 +37,9 @@ class ReactGraph extends PureComponent {
|
||||
onChange={onChange}
|
||||
nodeTypes={this.nodeTypes}
|
||||
/>
|
||||
<GlobalKeyHandler
|
||||
onNodeRemove={onNodeRemove}
|
||||
/>
|
||||
{children}
|
||||
</Provider>
|
||||
</div>
|
||||
@@ -44,7 +48,8 @@ class ReactGraph extends PureComponent {
|
||||
}
|
||||
|
||||
ReactGraph.defaultProps = {
|
||||
onNodeClick: () => {},
|
||||
onNodeClick: () => {},
|
||||
onNodeRemove: () => {},
|
||||
onLoad: () => {},
|
||||
onMove: () => {},
|
||||
onChange: () => {},
|
||||
|
||||
+14
-1
@@ -1,7 +1,8 @@
|
||||
import {
|
||||
UPDATE_TRANSFORM, UPDATE_SIZE, SET_NODES, SET_EDGES,
|
||||
UPDATE_NODE_DATA, UPDATE_NODE_POS, INIT_D3, FIT_VIEW,
|
||||
UPDATE_SELECTION, SET_SELECTION, SET_NODES_SELECTION
|
||||
UPDATE_SELECTION, SET_SELECTION, SET_NODES_SELECTION,
|
||||
SET_SELECTED_NODES_IDS, REMOVE_NODES
|
||||
} from './index';
|
||||
|
||||
export const updateTransform = (transform) => {
|
||||
@@ -65,6 +66,18 @@ export const setNodesSelection = ({ isActive, selection }) => {
|
||||
return { type: SET_NODES_SELECTION, payload: { nodesSelectionActive: isActive, selection } };
|
||||
};
|
||||
|
||||
export const setSelectedNodesIds = (ids) => {
|
||||
const idArray = Array.isArray(ids) ? ids : [ids];
|
||||
|
||||
return { type: SET_SELECTED_NODES_IDS, payload: { selectedNodeIds: idArray } };
|
||||
};
|
||||
|
||||
export const removeNodes = (ids) => {
|
||||
const idArray = Array.isArray(ids) ? ids : [ids];
|
||||
|
||||
return { type: REMOVE_NODES, payload: { ids: idArray } };
|
||||
}
|
||||
|
||||
export const updateSelection = (selection) => {
|
||||
return {
|
||||
type: UPDATE_SELECTION,
|
||||
|
||||
@@ -13,6 +13,8 @@ export const FIT_VIEW = 'FIT_VIEW';
|
||||
export const UPDATE_SELECTION = 'UPDATE_SELECTION';
|
||||
export const SET_SELECTION = 'SET_SELECTION';
|
||||
export const SET_NODES_SELECTION = 'SET_NODES_SELECTION';
|
||||
export const SET_SELECTED_NODES_IDS = 'SET_SELECTED_NODES_IDS';
|
||||
export const REMOVE_NODES = 'REMOVE_NODES';
|
||||
|
||||
export const initialState = {
|
||||
width: 0,
|
||||
@@ -98,12 +100,24 @@ export const reducer = (state, action) => {
|
||||
|
||||
return { ...state, ...action.payload, selectedNodesBbox };
|
||||
}
|
||||
case REMOVE_NODES: {
|
||||
const { ids } = action.payload;
|
||||
const nextEdges = state.edges.filter(e => !ids.includes(e.data.target) && !ids.includes(e.data.source));
|
||||
const nextNodes = state.nodes.filter(n => !ids.includes(n.data.id));
|
||||
|
||||
console.log(ids,);
|
||||
console.log( state.edges, nextEdges, );
|
||||
console.log(state.nodes, nextNodes);
|
||||
|
||||
return { ...state, nodes: nextNodes, edges: nextEdges };
|
||||
}
|
||||
case SET_NODES:
|
||||
case SET_EDGES:
|
||||
case UPDATE_TRANSFORM:
|
||||
case INIT_D3:
|
||||
case UPDATE_SIZE:
|
||||
case SET_SELECTION:
|
||||
case SET_SELECTED_NODES_IDS:
|
||||
return { ...state, ...action.payload };
|
||||
default:
|
||||
return state;
|
||||
|
||||
Reference in New Issue
Block a user