feat(keyhandler): add onNodeRemove callback

This commit is contained in:
moklick
2019-07-24 16:44:26 +02:00
parent 7b32ee660c
commit 4be80dd985
8 changed files with 166 additions and 43 deletions
+19
View File
@@ -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;
}
+11 -1
View File
@@ -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,
+8 -6
View File
@@ -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
View File
@@ -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
View File
@@ -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,
+14
View File
@@ -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;